课堂摘要
1.只要类中有指针,必有big three。
2.自我幅值这个动作需要检测是否存在重复赋值的情况。
3.构造函数一般不显式调用,而在创建一个对象时,构造函数被自动调用。
4.默认构造函数很重要,若没有则无法构造数组。
5.如果一个类没写任何构造函数,则编译器隐含地为其生成。
6.构造时,成员被依次构造(从前到后);先执行成员的构造函数在执行自己的,即123321.
7.析构时,成员被依次析构(从后到前);先执行自己的析构函数,再执行成员的。
8.若new时用了[ ],delete时也要用[ ].
9.用new申请的内存,必须用delete释放。
10.class类型必须用new/delete创建,销毁。
11.含有默认构造函数的类,才可能用new [ ]一次性地创建多个对象。
12.拷贝构造函数从来不显式调用,而是用编译器隐式调用。
13.所谓深度拷贝即去拷贝数据而不是指针。
14.友元的权利:可以无限访问该类的所有成员。
15.new是先分配memory再调用ctor。
设计模式
可以看出C++的设计模式种类繁多,也是之后需要学习的重点所在。
Big Three Function Design - 三大函数设计
C++三大函数:
析构函数
复制构造函数
operator=
析构函数
函数模样:~S()
当一个对象超出作用域或执行delete的时候,析构函数就被调用。
复制构造函数
函数模样:S(const S& s)
以下情况,复制构造函数均会被调用:
声明的同时初始化:
S s1 = s2; //注意此时虽然出现=,但是不是调用operator=哦
S s1(s2);
调用函数时使用按值传递(而不是按引用传递)
void f(S s);
S s1;
f(s1);
通过值返回对象
S f()
{
S s1;
return s1;
}
operator=
函数模样:const S& operator=(const S& s)
当=应用于两个已经构造的对象时,就调用复制赋值运算符operator=。
S s1;
s1 = s2; //注意与S s1 = s2; 的区别
注意事项:
三大函数不手动实现的时候,会使用默认的版本。比如operator=的默认版本会依次为数据成员复制,如果是基本数据类型自然没什么问题,但当数据成员含有指针的时候,operator的只会进行浅复制,即只是指针本身被复制,而不是指针所指向的内容被复制。见下例。
class S
{
public:
int *p;
S(int x=0){p=new int(x);}
};
void f()
{
S s1(2), s2;
S s3 = s1;
s2 = s1;
*s1.p=3;
cout << *s1.p << ' '<< *s2.p << ' ' << *s3.p << endl;//3 3 3
}
很明显这不是我们想要的,我们想让不同的对象的值不互相影响,此时需要实现深复制,见下例。
class S
{
public:
int *p;
S(int x=0){p=new int(x);}
S(const S& rhs)
{
p = new int(*rhs.p);
}
const S& operator=(const S& rhs) //rhs=right-hand side
{
if (this != &rhs) //检查是否复制自身
*p = *rhs.p;
return *this;
}
};
void f()
{
S s1(2), s2;
S s3 = s1;
s2 = s1;
*s1.p=3;
cout << *s1.p << ' '<< *s2.p << ' ' << *s3.p << endl; //3 2 2
}