写在前面
国庆去放松去啦,所以书本的学习就停格在了第十二章,也就是动态内存的地方,关于只能指针这一方面,我将联系放在了我设计的牛顿插值法。alloc这块,我的练习就暂且放一下,因为本章主要讲了如何管理内存的原理与思想,主要介绍了几种非常安全的C++内存管理方式。
那么终于要到了第三部分——”高级部分“!那我们鼓足勇气开始吧!
拷贝控制
你以为你以为的构造函数就单单在对象生成时可以使用这么简单吗?我之前的确是这样认为的哈哈哈,不就是重载么,有什么稀奇的哈哈,结果看完本章,醍醐灌顶!
组成拷贝控制操作分为以下五类:
1.拷贝构造
2.拷贝赋值运算符
3.析构函数
(上面三个被称作Big Three,哈哈哈怪名字)
4.移动构造函数
5.移动构造运算符
(后面这两个是C++2.0新加的)
书本上这个被称作是三/五法则,这些函数就是你不写,编译器也会帮你写上之前的三个(不确定)。但是,编译器的这番操作是为了类的完整性,但是这些函数并不是你想要的那样的功能,或者说这些函数并不是你想要的呢(C++2.0)那么,你就需要自己去定义一些构造以及析构函数,让这些obj生来就赢在起跑线上!
拷贝构造函数与运算符
struct A
{
foo();
foo(const foo&);
foo& operator = (const foo&);
private:
string str="wsnbb";
int i;
}
这个A类呢,拥有两个构造函数,第一个没有参数是显式的表示出默认的构造函数,也就是我们伟大的编译器自己去定义的
那么默认的构造函数有什么用呢?
默认构造函数就是仅仅为了帮助建立该obj而存在,也就是初始化数据成员,A类当中就是将str初始化为wsnbb字符串。如果没有初始化呢,那么编译器也不会为其进行初始化,str除外啦,比如 i 就会是未定义而造成无法访问。
那么我们现在就需要进行自己的定义,我们可以通过构造函数的初始化列表来跳过私有对象已有初始化的步骤,也可以在函数块当中定义进行赋值。这就是我们自己定义的构造函数。
那么A的第二个函数我们叫做拷贝构造函数,可以将同类的obj的数据拷贝到当前obj当中,这个家伙等价于:
A::foo(const foo& f)
{
this->str=f.str;
this->i=f.i;
}
那么为啥子要传引用呀?
原因其实非常简单:拷贝构造函数是用来初始化非引用类型的参数
拷贝构造运算符功能其实是一样的,为了可以与简单的数值运算相一致,也就是可以A=B=C这样子的操作,我们使其返回的值也是左值。这等价于:
foo& A::operator = (const foo& f)
{
this->str=f.str;
this->i=f.i;
return *this;
}
这就是拷贝构造的简单原理与用法。
explicit 禁止隐式类型转换
这个是C++语言对于编译当前的一种限制,防止不必要的隐式转换,比如C++2.0出现之前,0可以作为空指针的表示,但是为了防止混淆,引入了nullptr关键字,这样当你使用explicit函数时,0就不能被转换成空指针的代表,而它只能被当作零存在。这个道理同样在构造函数当中存在。
析构函数
析构函数,就当obj在离开作用域时,编译器自动的调用的一个函数,默认的情况下进行释放栈内存,但不会释放堆内存,就是需要使用delete或者free的内存,这时就需要进行新的定义。
神想法:=default,=delete
=default只能用于构造函数当中不过可以让编译器保留默认生成的构造函数们,当我们重写一个构造函数时,默认的构造函数就不能使用了,C++2.0给我们带来了新的想法,=default这个东西看似虽小啊,但是C++2.0的种种细小变化,让这门语言散发出新的生机,更加的强壮与矫健。
=delete这个关键字可以作用于任何函数当中,此词一出,这个函数便失效了,但是有一个例外——析构函数,这个函数虽然没有阻止你去使用=delete关键字,但是我们之后再使用delete obj时就会出现错误,这是内存泄露的大灾难呀!
当然也有一些情况会触发=delete:
析构函数为私有的;
当不能拷贝赋值或者销毁类成员时,类的合成拷贝控制成员就被定义为=delete
本章给你了一个对象的出生与死亡的操作的方法,需要好好学鸭!