对于一个没有实例化的空类,编译器不会给他默认任何函数,当实例化一个空类后,编译器将根据需要生成相应的函数,包括:
* 构造函数
* 析构函数
* 拷贝构造函数
* 赋值运算符号
* 地址操作符
构造函数
默认构造函数
默认构造函数是在未提供显示初始化值时,被用来创建对象的构造函数。它可能是如下形式的。
stock::stock(){}
//stock是一个类
如果为类定义了一个构造函数,那么也必须定义默认构造函数,否则编译器将提示出错,就算编译器报错,也不应该这样做,因为未初始化的值都是未定义的。
在初始化对象时,类成员初始化顺序一定是按照声明的顺序来初始化的
#include<iostream>
using namespace std;
class test{
public:
int a;
int b;
test(int val):b(val),a(b){}
};
int main(void)
{
test a(1);
cout<<"a.a="<<a.a<<endl;
cout<<"a.b="<<a.b<<endl;
return 0;
}
输出:
a.a=0
a.b=1
可以看到a.a并没有初始化为1,原因就是a先于b初始化,而此时b的值是未定义的。
派生类的构造函数
如果一个派生类没有自定义任何构造函数而基类有,派生类的默认构造函数将按照声明顺序使用基类构造函数。
#include <iostream>
using namespace std;
class basic1{
public:
int a;
basic1():a(1){ cout<<"basic1 initialized"<<endl; }
};
class basic2{
public:
int a;
basic2():a(2){ cout<<"basic2 initialized"<<endl; }
};
class son:public basic2,public basic1{
};
int main ()
{
son a;
cout<<"a.basic1::a: "<<a.basic1::a<<endl;
cout<<"a.basic2::a: "<<a.basic2::a<<endl;
}
输出:
basic2 initialized
basic1 initialized
a.basic1::a: 1
a.basic2::a: 2
在上面的代码中,我们发现可以使用作用域的方式方位两个父类的同名变量
析构函数
在销毁对象时自动执行的函数,与类名相同,加一个~,析构函数一般用于释放构造函数申请的资源。
析构函数的调用顺序与构造函数相反。
拷贝构造函数/复制构造函数
复制构造函数用于将一个对象复制到新建的对象中,一般原形通常如下:
class_name(const class_name &)
- 新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用。
- 默认的复制构造函数逐个复制非静态成员(浅复制)
浅复制是不安全的,尤其是类里面还有指针变量的时候
#include<iostream>
#include<string.h>
using namespace std;
class man{
private:
char *name;
public:
man():name(NULL){}
man(char *str);
void showname(){cout<<name<<endl;}
~man(){ delete[] name;}
};
man::man(char *str)
{
int len=strlen(str);
name = new char[len+1];
strcpy(name,str);
}
void fun(const man &val)
{
man bval=val; //这里故意声明另一个类变量触发拷贝构造函数
bval.showname();
//函数结束后,bval的析构函数将被调用
}
int main(void)
{
char name[] = "lin";
man a(name);
a.showname();
fun(a);
a.showname();
cout<<"end"<<endl;
while(1);
//这里必须阻止主函数结束
//因为fun函数结束以后,a的name指向的内存已经被释放掉了
//再释放就出现内存错误了
return 0;
}
输出:
lin
linend
可以看到,当第三次显示名字的时候出现了问题,原因就是浅拷贝bval的时候只是拷贝的name指针的指向,而没有将指向的内存拷贝
继承带有复制构造函数的基类
如果一个类继承自一个带有拷贝构造函数的基类的话,那么编译器在为其合成拷贝构造函数的时候会调用基类的拷贝构造函数。
带有虚函数类对象的拷贝构造函数
虚函数表是在编译的时候创建的,同类对象初始化时,采用浅拷贝已经够用了,类似于字符串指针指向字符串常量也是安全的。
子类在继承父类的虚函数时,会将父类的虚函数表拷贝一份到自己的虚函数表