类的构造(1):编译器自动添加的4函数
假如我们这样简单的定义一个类
class Arwen{
public : int age;
}
然后实例化它 Arwen wen;
虽然看这个类看起来什么函数也没有,但是编译器一般会默认添加4个函数进去,分别是如下4个函数
无参构造函数
析构函数
复制构造函数
重载赋值运算符
无参构造函数
如果你自己显示写了构造函数的话编译器就不再会给你自动添加一个无参构造函数进去了.
另外我们实例化一个类对象时,执行的顺序是:先调用父类的构造函数,再调用成员变量中的类对象构造函数,最后才调用自己的构造函数.
有类class job{
public:
job(string name){ }
};
class Arwen{
public:
job myJob;
Arwen() :myJob( "developer"){ //如果不在初始化列表中显式调用job的有参构造函数就会出错.因为job中没有自动添加的无构构造函数了
}
}
一个参数构造函数特殊用法
带一个参数的有参构造函数有个特殊的用法.例如假如类Arwen有构造函数Arwen( int age) { }
则可这样实例化Arwen wen = 25; //等价于Arwen wen(25);
如果你不希望出现上面这种隐式的转换,则可这样去写构造函数
explicit Arwen(int age) { }
这样一来再出现Arwen wen = 25这样的写法编译器就报错了.
析构函数
实际上我们一般用构造函数用的多.剩下的三种函数较少用.一般可不这与就让编译器就自动添加就行了.
一般是成员变量中有指针时才会需要显式的写析构函数,当需要析构函数时一般也需要复制构造函数和重载赋值操作符,这三个基本上是连在一起出现的.
析构函数一般就是释放内存的了,例如:
class Arwen{
int* pNum;
int* pArray;
public:
Arwen(){
pNum = new int(88);
pArray = new int[4];
}
~Arwen(){
delete pNum;
delete[] pArray; //虽然它前面声明的时候看起来跟pNum一样,但分配内存时实际上是指向一个数组了.
}
另外如果在类继承层次中一般把析构函数写成virtual函数.这样实例化不同的类后才能调用正确的析构函数去释放内存.
复制构造函数
复制构造函数实际上就是一种特殊的构造函数,其实也不太特殊,你就把它看成构造函数得了.只不过这个构造函数的参数必须是类对象而已.例如
Arwen(const Arwen& wen){
age = wen.age
}
所以你可以这样实例化时就是调用复制构造函数:
Arwen an;
Arwen wen(an) ;
Arwen wen = an; //会自动转换成Arwen wen(an);当然如果你闲得蛋痛硬是要把复制构造函数整成explicit Arwen(const Arwen& wen){ }那自然不能这样用了.只能老实的用Arwen wen(an);
重载赋值操作符
其实重载赋值操符,然后对它调用.作用和复制构造函数非常类似的.复制构造函数是一次完事,用构造函数构造出一个对象,把另一个对象的值全部拷过来.
而赋值操作的话则必须是相当于分两步完成的.第一步是之前必须是已经实例化出一个对象了,然后才能用其他的对象来赋值给它.例如
Arwen& operator= (const Arwen& wen){
age = wen.age;
return *this;
}
于是你这样操作的时候:
Arwen an;
an.age = 25;
Arwen wen;
wen = an; //这里就是调用重载的赋值操作符了.如果你直接这样写Arwen wen = an;这可不会去调用重载的赋值运算符,而是调用复制构造函数去了.
实际上重载赋值操作符中的参数也可以随便写的了,只不过这样用处一般不大,很少去用了.例如
Arwen& operator=( int num){
age = num;
return *this;
}
你可以这样用
Arwen wen;
wen = 25; //这里就是调用重载的赋值操作符