构造函数和析构函数
1 首先,C++有拷贝构造函数 拷贝赋值运算符 移动构造函数 移动赋值运算符和析构函数
构造函数可以先分为:默认构造函数和拷贝构造函数,如果我们在代码中不显示的自己声明构造函数的话,c++的编译器会为我们默认生成默认构造函数和拷贝构造函数
1 针对默认构造函数
默认构造函数可以使用两种方式实现:1 赋值,2 列表初始化
2 针对拷贝构造函数
拷贝构造函数分为深拷贝和浅拷贝
浅拷贝是只是复制内容,原对象的操作会影响现有的实例化的对象
深拷贝不仅仅复制内容,是复制内容并且拷贝到另一份新的空间,两个实例化的对象不会互相干扰互相影响
c++编译器在我们没有显示的实现拷贝构造函数的时候默认是浅拷贝
浅拷贝容易有bug,如果存在引用值或者指针的时候,不重载拷贝构造函数和拷贝赋值运算符的话,在对象销毁的时候,析构函数会多次释放指向的对象,导致错误。
3 关于析构函数
析构函数没有函数返回类型和形参,析构函数可以是虚函数,但是析构函数是不允许重载的,因为虚构函数没有返回值类型和形参。子类的虚构函数可以继承基类的虚构函数并进行重写
关于什么时候需要自己实现拷贝构造函数和拷贝赋值运算符
一般来说我们会声明默认构造函数和析构函数,c++编译器目前会默认的给我们定义合成拷贝构造函数和合并赋值运算符。合成拷贝构造函数只是默认的复制类的成员变量,如果出现指针,引用等类型时,因为没有显示的去重载拷贝构造和复制运算符,在析构函数时,指针会指向同一对象,然后该对象已经被销毁了,再次销毁会出现意向不到的错误。
因此,我们可以根据是否在析构函数体内部需要对指针或者引用做出释放等操作时,去决定是否需要重载拷贝构造函数和复制运算符。反之亦然。
如果需要重载拷贝沟道函数,那么也要重载复制运算符。反之亦然。
class HasPtr {
public:
HasPtr(const std::string& s = std::string()) : ps(new std::string(s)), i(0) { }
HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) { } //拷贝的是指针指向的对象
HasPtr& operator=(const HasPtr& rhs_hp) {
if(this != &rhs_hp) {
std::string *temp_ps = new std::string(*rhs_hp.ps); //先拷贝到一个临时指针中,但是指针分配的动态内存不是临时的。
delete ps; // delete 时不用加 *
ps = temp_ps; // 让 ps 指向分配的动态内存。
i = rhs_hp.i;
}
return *this;
}
~HasPtr() { delete ps; }
private:
std::string *ps;
int i;
}
关于default和delete
default :只允许c++编译器能够生成默认的构造函数声明加上default,意思是这个变为内联函数并且由c++编译器默认生成。
我们的拷贝构造函数 拷贝赋值运算符 默认构造函数 析构函数均可以使用default
如果在类内使用 =default,合成的函数将隐式地声明为内联函数。如果不希望合成内联函数,则应该在类外定义处使用 =default
只能对默认构造函数或拷贝构造成员这些具有合成版本的函数使用 =default
delete:通过delete声明告诉我们,这个函数不能够被外界对象所使用
删除的函数的性质:虽然声明了它们,但是不能以任何方式使用它们。
定义为删除的函数的方式:在参数列表后加 =delete;
在智能指针shared_ptr实现的时候是私有化构造函数和赋值运算符,也可以通过delete声明
Student(const Student&) = delete;//阻止拷贝
=delete 只能出现在函数第一次声明的时候,并且可以对任何函数指定 =delete