构造析构/拷贝构造/拷贝赋值
目录
三个特殊函数
|
|
构造函数和析构函数
-
代码演示
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15inline String::String(const char* cstr = 0) { if (cstr) { m_data = new char[strlen(cstr)+1]; strcpy(m_data, cstr); } else { m_data = new char[1]; *m_data = '\0'; } } // c里面字符串是以 '\0' 为结束符号 这是构造函数 inline String::~String() { delete[] m_data; } // 析构函数 因为上述构造函数 为str分配了一个内存 所以要释放内存 要不然会内存泄漏 // class 有指针 多半要做动态分配 所以就要在析构函数 释放内存
-
-
构造函数析构函数里能不能抛出异常
-
C++只会析构已经完成的对象,对象只有其构造函数执行完毕才算是完成,在构造函数中发生异常,控制权转交给析构函数之外。因此,在对象b的构造函数中发生异常,并不会调用对象b的析构函数,所以会造成内存泄漏
用 auto_ptr 对象来取代指针类成员,便对构造函数做了强化,免除了抛出异常时发生资源泄 漏的危机,不再需要在析构函数中手动释放资源
如果控制权基于异常的因素离开析构函数,而此时正有另一个异常处于作用状态,C++ 会调 用 terminate 函数让程序结束
-
如果异常从析构函数抛出,而且没有在当地进行捕捉,那个析构函数便是执行不全的。如果析构函数执行不全,就是没有完全他应该做的每一件事
-
深拷贝与浅拷贝
-
什么是浅拷贝
-
拷贝后,两个对象指向同一个内存地址,如果修改了其一个,另一个也会受到影响
1 2 3String a("Hello"); // 这个时候 a 的 data 指向了 'Hello\0' 的地址 String b("World"); // 这个时候 b 的 data 指向了 ‘World\0’ 的地址 b = a; // 这个时候 b 就会指向 'Hello\0' 的地址 a&b都指向了同一个地址 可是 'World\0' 还在 造成内存泄漏 而且你改a b就会受到影响 所以这种 'b = a' 叫做浅拷贝 -
出现类的等号赋值时,会调用拷⻉函数,在未定义显示拷⻉构造函数的情况下, 系统会调 用默认的拷⻉函数-即浅拷⻉,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷⻉是可行的,但当数据成员中有指针时,如果采用简单的浅拷⻉,则两类中的两个指针指向同一个地址,当对象快要结束时,会调用两次析构函数,而导致野指针的问题
-
-
什么是深拷贝
- 拷贝后,两个对象的值相同,但是不是同一个内存地址
-
简而言之,当数据成员中有指针时,必需要用深拷⻉更加安全
拷贝构造函数
-
什么是拷贝构造函数及实现
-
1 2 3 4inline String::String(const String& str) { // 为什么叫构造 因为这是一个构造函数 为什么叫拷贝 因为传参是自己 m_data = new char[strlen(str.m_data)+1]; // 直接取另一个object的private data strcpy(m_data, str.m_data); } // 这也是深拷贝
-
-
什么情况会调用拷贝构造函数
- 一个对象以值传递传入函数体,需要拷贝构造函数创建一个临时对象压入到栈空间
- 一个对象以值传递的方式从函数返回,需要执行拷贝构造函数创建一个临时对象作为返回值
- 一个对象需要通过另一个对象进行初始化
-
为什么拷贝构造函数必须是pass by reference,不能是 pass by value
- 是为了防止死递归
- 当一个对象需要以值方式进行传递时,编译器会生成代码调用它的拷⻉构造函数生成一个副本,如果类 A 的拷⻉构造函数的参数不是引用传递,而是采用值传递,那么就又需要为了创建传递给拷⻉构造函数的参数的临时对象,而又一次调用类 A的拷⻉构造函数,这就是一个死递归
拷贝赋值函数
|
|
Effective c++ Item
- 对于更多的三个特殊函数一些规范及其知识点,请看《Effective c++ Item5~Item12》