默认构造函数:
class testClass
{
public:
testClass(); /* 默认构造函数 */
testClass(int a, char b); /* 构造函数 */
testClass(int a=10,char b='c'); /* 默认构造函数 */
private:
int m_a;
char m_b;
};
- 默认构造函数主要是用来完成如下形式的初始化的:
testClass classA;
// 或者 testClass *classA = new testClass;
在这种情况下,如果没有提供默认构造函数,编译器会报错;
非默认构造函数在调用时接受参数,如以下形式:
1testClass classA(12,'H');
2//或者 testClass *classA = new testClass(12,'H');
如果程序猿没有定义任何构造函数,则编译器会自动定义默认构造函数,其形式如 testClass() {}; 可以看出,编译器自动提供的默认构造函数是 啥也没有啊 ;
定义默认构造函数有两种方式,如上述代码展示的,一是定义一个无参的构造函数,二是定义所有参数都有默认值的构造函数 ;
注意:一个类只能有一个默认构造函数!也就是说上述两种方式不能同时出现,一般选择 testClass(); 这种形式的默认构造函数 ;
只要程序猿定义了构造函数,编译器就不会再提供默认构造函数了,所以,程序猿最好再手动定义一个默认构造函数,以防出现 testClass a; 这样的错误。
什么时候调用默认构造函数?
1.如果一个类没有默认构造函数,但是这个类中含有一个另外一个类的对象,并且这个类含有默认构造函数,则编译器会为该类合成一个默认构造函数,例如:
class Foo
{
public:
...
Foo();
Foo(int);
...
}
class Bar
{
public:
char *str;
Foo foo;//Foo类含有默认构造函数,所以编译器会为Bar生成一个默认构造函数,但是这个构造函数并不会去出初始化str,这个str指针需要程序员去初始化
//编译器合成的默认构造函数可能会是这样:
Bar::Bar()
{
foo.Foo::Foo(); //伪代码,这只是为了满足编译器的需要
}
}
如果程序员在Bar中定义了构造函数,则编译器不会再去为Bar生成默认构造函数,而是在Bar的构造函数中插入一些代码,比如:
Bar::Bar()
{
foo.Foo::Foo();//编译器自己插进去的必须放在str初始化之前
str=nullptr;
}
2.一个带有默认构造函数的基类派生出一个没有默认构造函数的子类,编译器会为子类生成一个默认构造函数,在这个构造函数中调用基类的默认构造函数。
3.继承自一个带有虚函数的父类,因为需要在子类中处理相关的虚函数表,所以需要为子类生成一个默认构造函数,初始化虚函数表
4.子类虚继承基类,此处比较复杂,详见《深度探索c++对象模型》46页
拷贝构造函数的调用时机:
1当用类的一个对象初始化该类的另一个对象时.例如:
int main()
{
point A(1,2);
point B(A);//用对象A初始化对象B,拷贝构造函数被调用.
}
2 如果函数的形参是类的对象,调用函数时,进行形参和实参结合时.
void f(point p)
{
}
main()
{
point A(1,2);
f(A);//函数的形参为类的对象时,当调用函数时,拷贝构造函数被调用.
//编译器做的工作:
//point p(A); 这里调用point的拷贝构造函数,生成一个临时的p对象,实行的是浅拷贝
//f(p); f函数结束后,p对象中new的内存将会被释放掉,此后再调用相关对象A的操作将会出错,因为浅拷贝的内存已经被释放了,所以f函数最好是传递一个引用进去
}
3 如果函数的返回值是类的对象,函数执行完成返回调用者时.
point g()
{
point A(1,2);
return A;//函数的返回值是类的对象,返回函数值时,调用拷贝构造函数.
//编译器的工作:
//void g(point &p) //讲返回值设置空,并给参数加一个point的引用
//{
// point A(1,2);
// ...
// p.point::point(A);//返回前调用拷贝构造函数用A初始化p;
// return;
//}
}
void main()
{
point B;
B = g();
}
4、需要产生一个临时类对象时。