对象拷贝时的编译器优化:
- 现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可传能减少一些传参和参过程中可以省略的拷贝。
- 如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流的相对新一点的编译器对于连续一个表达式步骤中的连续拷贝会进行合并优化,有些更新更“激进”的编译还会进行跨行跨表达式的合并优化。
在这个样例中,原本的程序应该是在f2函数中构造aa因为aa出函数会销毁于是拷贝构造一个中间体进行返回后析构aa并通过中间体打印后析构,而在编译器的优化中直接构造中间体返回后进行打印并析构,将构造aa和析构优化掉了。
例题:
关于new申请的空间失败问题:
我们都知道,使用 malloc/calloc 等分配内存的函数时,一定要检查其返回值是否为“空指针”(即检查分配内存的操作是否成功),这是良好的编程习惯,也是编写可靠程序所必需的。但是,如果你简单地把这一招应用到 new 上,那可就不一定正确了。
对于new申请的空间如果失败了那么就会抛异常跳过后面的代码。如果你想检查 new 是否成功,应该捕捉异常。
void func()
{
// throw try/catch
int n = 1;
while (1)
{
//throw try/catch
void* p1 = new char[1024 * 1024];
cout << p1 << "->" << n << endl;
++n;
}
}
int main()
{
try
{
func();
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
在这段代码中通过不停的new申请空间直到抛异常后捕获异常并打印原因
同时我们也可以通过跳转到汇编去稍微看一下它的底层。
对于写了析构函数的类系统在开空间时候会在头上多开4个字节来记录个数来调用析构函数。、
例子:看下面一段代码。
类A和B的内存大小都是8字节,在运行此代码时报错是为什么。
对于p2和p3开辟的空间如同,开辟空间后对p3来说返回的位置如图(因为p2没有写析构函数同时也没有动态申请的空间需要析构于是编译器就优化掉了,对于p2的delete也就没有问题)。当时对于p3来说在头部有额外开辟的空间 ,加了方括号才会向前偏移到正确的位置释放,而不加方括号就会在如图位置析构一次,而开辟的空间是不能部分析构的所以会报错。
同理如果B类写了析构函数而delete不加方括号也会报错。
如此修改就不会报错: