知乎C++问题整理
来源:互联网 发布:35毫米自行高炮知乎 编辑:程序博客网 时间:2024/05/21 19:22
如何兼顾性能,合理选择C++STL容器?
ANSER:
首先要搞清楚,如果STL
有性能问题,那么问题出在哪里?
STL
可以简单地认为就是算法+数据结构,所有容器的算法选择和实现都是经过精心设计和严格测试的,几个主流STL
实现都不会有大问题。
性能问题通常都出在内存数据操作上,内存操作有三种,内存读取、内存复制和内存分配。
所以选择合适容器的依据就是要尽量减少内存操作尤其是复制操作,比如频繁中间插入删除就不要选Vector
,频繁随机访问就不要选list
。
除了选错容器这种低级错误外,性能瓶颈基本都是出在容器内的对象身上,解决方案:
- 在容器内放对象指针,而不是实例,对象生命周期自己管理,只有在真的有性能问题时才考虑这样做,因为内存泄漏的风险还是挺高的。
- 自己定义
allocator
,实现对象内存池,只有在确认内存分配是瓶颈时才用。 - 不拿两个容器实例做赋值操作,传入参数用指针或引用,传出的参数用
swap
来实现容器数据传递,务必确认自己清楚知道在做什么。
关于C++ 数组和指针的问题?
对数组取地址时,数组名不会被解释为其地址。等等,数组名难道不被解释为数组的地址吗?不完全如此:数组名被解释为其第一个元素的地址,而对数组名应用地址运算符(即&)时,得到的是整个数组的地址:
short tell[10]; //声明一个长度为20字节的数组(short型变量大小为2字节)cout << tell << endl; //显示&tell[0]cout << &tell << endl; //显示整个数组的地址
从数字上说,这两个地址相同;但从概念上说,&tell[0]
(即tell
)是一个2字节内存块的地址,而&tell
是一个20字节内存块的地址。因此,表达式tell+1
将地址值加2,而表达式&tell+1
将地址值加20。换句话说,tell
是一个short
指针(short*
),而&tell
是一个指向包含10个元素的short
数组的指针(short(*) [10])
。
您可能会问,前面有关&tell
的类型描述是如何来的呢?
首先,您可以这样声明和初始化这种指针:
short (*pas) [10] = &tell; //pas指向一个有10个short元素的数组
如果省略括号,优先级规则将使pas
先与[10]
结合,导致pas
是一个包含10个short
型指针的数组,因此括号是必不可少的。
其次,如果要描述变量的类型,可将声明中的变量名删除。因此,pas
的类型为short(*) [10]
。
另外,由于pas
被设置为tell
,因此*pas
与tell
等价,所以(*pas) [0]
为tell
数组的第一个元素。
回到问题本身,当int
型变量的大小为4字节时,arr
是一个4字节内存块的地址,而&arr
是一个40字节内存块的地址。虽然这两个内存块的起始位置相同,但是大小不同。
题主可以在代码里加上这两行,对比一下输出结果:
cout << arr + 1 << endl; //地址+4cout << &arr + 1 << endl; //地址+40
示例代码:
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; cout << &(arr[0]) << endl; cout << arr << endl; cout << &arr << endl; cout << arr + 1 << endl; cout << &arr + 1 << endl;
输出:
0013FE600013FE600013FE600013FE640013FE88请按任意键继续. . .
C/C++ 中 0 与 NULL 区别是什么?用 delete 时,用 p=0,还是用 p=NULL 好?为什么?
ANSER:
首先呢,要明白一点儿,NULL
是一个无类型的东西,而且是一个宏。而宏这个东西,从C++
诞生开始,就是C++
之父嗤之以鼻的东西,他推崇尽量避免宏。而在他的FAQ
中,也有相应的一个关于NULL
与0
的解释,也谈到了这一点儿。
在C++
标准中,我们可以见到一个词语叫做null pointer constant
,其实在C++11
标准前,是只承认0
为null pointer constant
的。所以,在C++
中,我们也经常能听到一个说法,就是赋予null pointer
,应该是使用0
,而非NULL
。而nullptr pointer constant
这个词语在C++11
发布后,终于再添了一个成员,就是nullptr
。而与NULL
本质不同的是,nullptr
是有类型的(放了在stddef
头文件中),类型是 typdef decltype(nullptr) nullptr_t
; 而正是因为是有类型的,这给我们编译器实现nullptr
的时候带来了更多细节的考虑,当然也给了使用者更多的保障,所以如果你的编译器支持nullptr
,请一定使用nullptr
!
而nullptr
的出现背景,其实是很简单的,C++
哲学上来说就是C++
之父一直对null pointer
没有一个正式的表示感到非常不满,而更工程的来说,就是关于重载这个问题。
void f(void*){}void f(int){}int main(){ f(0); // what function will be called?}
而引入了nullptr
,这个问题就得到了真正解决,会很顺利的调到void f(void*)
这个版本。
好了,真的以为nullptr
就这样了么? 我前面说过了nullptr
是有类型的,叫做nullptr_t
,这给我们编译器实现带来了诸多要考虑的东西,不幸的话让我们来举点儿奇葩例子吧!
union U{ long i; nullptr_t t;};int main(){ U u; u.i = 3; printf("%ld\n",(long)u.t); // What it is? 0 or 3?}
那么这是应该符合union
语意还是nullptr
的语意呢?这在标准中是没有说的,我们也为此争论了非常久。当然在我们编译器的实现还是保持了nullptr
的语意,结果是0
。
而nullptr
有类型后,还能做什么呢?那当然就是可以捕获异常了。
int main(){ try { throw nullptr; } catch(nullptr_t) { } }
你扔一个NULL
试试?看他应该用什么收,正是因为没有类型,所以就要用它的本质类型,比如long
什么的来说。你扔一个0试试?那就也不是所谓的空指针类型了,就是要用int
什么的来收了。
所以,推崇nullptr
是有道理的,我们在编译器实现nullptr
的时候考虑了非常非常多的细节,还有很多你们可能一直用不到的情况,我们都要用来测试,目的就是保障开发者的使用。再次那句话,如果你的编译器支持nullptr
,请一定使用nullptr
!
最后再扯一点儿,0
在C++
是很神奇的东西。比如纯虚函数为什么是用=0
来设置的,不知道有没有同学去考虑过这个问题没有。如果你深刻理解了C++
哲学,这应该就是非常简答的问题了。学语言嘛,一定要学到其哲学,你才能知道其之美,其之威力,尤其是C++
。
如何计算带指针的结构体大小?
问题:
struct X{ char a; float b; int c; double d; unsigned e;};
由于存储变量时地址对齐的要求,所以这个结构体大小应该是32。
如果我多定义一个任意型的指针
struct X{ char a; float b; int c; double d; unsigned e; int *f;};
按照地址对齐的要求,这样结构体大小应该是40,但它仍然是32。
我再加一个任意型的指针
struct X{ char a; float b; int c; double d; unsigned e; int *f; double *g;};
结果这样结构体大小就直接变成40了。如果指针大小是地址总线大小的话,2个指针就是8字节,加上原来的32字节也刚好等于40字节,并且也满足存储变量时地址对齐的要求。但是为什么刚才加一个指针不变,两个就变了。
回答:
一开始的时候是这样的
struct X { char a; // 1 bytes char padding1[3]; // 3 bytes float b; // 4 bytes int c; // 4 bytes char padding2[4]; // 4 bytes double d; // 8 bytes unsigned e; // 4 bytes char padding3[4]; // 4 bytes};
加了一个指针以后是这样的
struct X { char a; // 1 bytes char padding1[3]; // 3 bytes float b; // 4 bytes int c; // 4 bytes char padding2[4]; // 4 bytes double d; // 8 bytes unsigned e; // 4 bytes int *f; // 4 bytes};
再加一个指针以后是这样的
struct X { char a; // 1 bytes char padding1[3]; // 3 bytes float b; // 4 bytes int c; // 4 bytes char padding2[4]; // 4 bytes double d; // 8 bytes unsigned e; // 4 bytes int *f; // 4 bytes double *g; // 4 bytes char padding3[4]; // 4 bytes};
这样就好懂多了吧?
轮子哥的回答,说明了原理
我来补充一下匿名用户的答案。第一个图是这样的
struct X { char a; // 1 bytes char padding1[3]; // 3 bytes float b; // 4 bytes int c; // 4 bytes char padding2[4]; // 4 bytes double d; // 8 bytes unsigned e; // 4 bytes char padding3[4]; // 4 bytes};
padding1
的存在是因为,offset(b)
必须能够被align(b)
整除,所以塞三个char
。
b
的偏移字节是它自身字节的整数倍,因此要添加三个字节的偏移
padding2
的存在是因为,offset(d)
必须能够被align(d)
整除,所以塞4个char
。
原因同上
对于所有基本类型,align(T)==sizeof(T)
,所以有了上面两条,
那align(X)
是多少呢?当然就是所有成员里面align
最大的那个,是align(d)==8
好了,因此sizeof(X)
必须能够被align(X)
整除,就有了padding3
。
整个结构体的大小必须能被其中最大
align
的树整除。
- 知乎C++问题整理
- 知乎收藏问题汇总
- python Requests 知乎问题图片爬虫
- ML && DL 知乎问题收集
- 一个知乎问题引发的血案
- 个人知乎 ##功能二-问题发布
- 知乎关于嵌入式若干问题的回复整理(转载)
- 知乎关于嵌入式若干问题的回复整理(转载)
- 如何自学 Android 编程-----整理stormzhang的知乎问答
- 程序员如何做到年薪 50 万?--知乎整理
- PKAV成员长短短 知乎Live资料整理
- (转)知乎:史上最全Quant资源整理
- git学习资料整理(知乎搜集的)
- 知乎
- 知乎
- 语句乎?表达式乎?(Python/C)
- 知乎——C语言的困惑
- 程序员是不是应该加强运动?(知乎同名问题)
- phpexcel yii 导入
- 有return的情况下try catch finally的执行顺序(最有说服力的总结)
- 无刷电动车控制器设计要点
- Android系统架构分析 和 Android应用程序组件介绍
- stdarg.cpp
- 知乎C++问题整理
- CC1110 Mini Development Kit 868-915 MHz
- 企业应用通用架构图
- 响应式和自适应的区别
- 2014 年华为校园招聘机试题
- Android中资源文件夹res/raw和assets的使用
- jqueyr proxy()
- 一致性DMA与流式DMA
- 汇编语言学习笔记(6)——包含多个段的程序