c++构造函数总结

构造函数:

(1)       构造函数的定义和作用:

构造函数用于对一个对象进行初始化操作。在对象被创建的时候,会自动调用构造函数(大多数情况下会被调用,请注意是大多数)。构造函数与类名相同,不能有返回值,甚至不能有return语句。不能对构造函数取地址。对于一般情况下,构造函数必须定义为public,尽管定义为private不会编译出错(如果不使用类),但是没法定义类的对象。没有定义构造函数的类,编译器会默认生成一个构造函数。不能在程序中自己手动调用构造函数

示例代码:

/*constructor*/

#include <iostream>

using namespace std;

 

 

class TTT

{

public:

   TTT();       

};

 

TTT::TTT()

{

   cout<<"constructor called"<<endl;

}

 

TTT global;  //全局变量

 

int main()

{

   cout<<"main begins"<<endl;

   TTT a;     //局部变量

   TTT* b=new TTT();      //堆上分配内存的对象

 

//         a.TTT();          //error!

 

   return 0;

}

(2)       构造对象的书写方式:

类的构造函数的书写和普通的内部类型(如int)的初始化写法一样,如int aint a=int(3)int a(3);对于类,同样有这三种书写方式。

方式一:TTT a;     方式二:TTT a=TTT();    方式三:TTT a();

方式三是方式二的简写形式,方式一是方式三的特例而已,如果类提供的构造函数都带有参数,那么没有方式一的写法(关于多个构造函数,即重载,参考下面)。所以实质上只有两种写法。对于有参数的构造函数,提供对应的参数列表即可。

但是请注意:不是上面的方式都会自动调用构造函数。方式三的写法并没有调用构造函数(如果自己实现构造函数,可以运行程序分析,对于默认的构造函数,目前我也不清楚有没有调用)。

!!注意:经过测试,发现,方式二的定义编译通过,但是具体含义不对,这种写法并没有创建一个TTT类的对象,因为如果给TTT定义方法,发现a.method()是不可行的,编译提示a不是一个类类型或union类型。但是对于有参数的构造函数,是可以这样定义的,如TTT a=TTT(1,3);所以方式二不适用于无参数构造函数(包括默认生成构造函数或者自定义无参数构造函数)。对于带默认参数的构造函数,也不能采用方式二定义,除非仍然加上了参数。

#include <iostream>

using namespace std;

 

 

class TTT

{

public:

   TTT();       

};

 

TTT::TTT()

{

   cout<<"constructor called"<<endl;

}

 

int main()

{

   cout<<"main begins"<<endl;

   TTT a;      //方式一

   TTT b();    //方式三         //没有调用构造函数

   TTT c=TTT(); //方式二

 

   TTT* d=new TTT;     //方式一

   TTT* f();           //方式三        //没有调用构造函数

   TTT* e=new TTT();   //方式二

  

   return 0;

}

(3)       关于编译器生成构造函数的问题:

对于没有写构造函数的类,编译器会自己生成构造函数(假设称为编译器构造函数)。一旦用户(程序员)定义了一个或多个构造函数(只要是构造函数),那么编译器就不会自动生成构造函数了。请注意:编译器构造函数!=默认构造函数,编译器构造函数!=无参构造函数。解释如下:

关于默认构造函数的解释下面将单独分析,这里只说明无参构造函数。编译器构造函数是无参数的,但是无参数的构造函数不一定是编译器构造函数,因为用户(程序员)也可以自己定义无参构造函数,用户定义无参构造函数,编译器也不会再生成构造函数。

/*示例代码*/

#include <iostream>

using namespace std;

 

 

class TTT

{

public:

   TTT(int a);

};

 

 

TTT::TTT(int a)

{

   cout<<a<<endl;

}

int main()

{

   /*

   由于定义了一个构造函数,编译器不再生产构造函数,所以除非自己定义一个无参

   构造函数,否则下面的写法编译出错。其中只有两种方式没有出错,是因为不会

   调用构造函数。

   */

   cout<<"main begins"<<endl;

//         TTT a;      //error

   TTT b();    //方式三         //没有调用构造函数

//         TTT c=TTT(); //error

 

//         TTT* d=new TTT;     //error

   TTT* f();           //方式三        //没有调用构造函数

//         TTT* e=new TTT();   //error

  

   return 0;

}

 

(4)       构造函数的重载(多个构造函数):

构造函数符合和一般的函数一样的重载规则。只要构造函数在参数类型上存在足够大的差异,编译器就能为每个使用选出一个正确的。

 

(5)       构造函数的默认参数

构造函数可以和一般函数一样定义默认参数,使用和一般的函数一样,比如可以全部采用默认参数或者部分默认参数,对于部分默认参数,默认的参数必须在参数列表后面等等。

(6)       关于默认构造函数:

默认构造函数(default constructor)就是在没有显式提供初始化式时调用的构造函数。它由不带参数的构造函数,或者为所有的形参提供默认实参的构造函数定义。如果定义某个类的变量时没有提供初始化式就会使用默认构造函数。

如果用户定义的类中没有显式的定义任何构造函数,编译器就会自动为该类型生成默认构造函数,称为合成的构造函数(synthesized default constructor)。

如果类包含内置或复合类型的成员,则该类就不应该依赖于合成的默认构造函数,它应该定义自己的构造函数来初始化这些成员。

事实上,关于默认构造函数还有一些更复杂的问题。从更深层次来说,在程序员没有定义默认构造函数的时候,编译器会自动生成默认构造函数的说法并不正确。只有在“需要时”编译器才会为我们生成默认构造函数。详细探讨可参考http://dev.firnow.com/course/3_program/c++/cppsl/2008222/100475.html

(7)       构造函数的初始化列表:

转载自:http://www.cppblog.com/baggio1984/archive/2007/09/30/33270.html

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。

Example::Example() : ival(0), dval(0.0) {}//ival dval是类的两个数据成员

上面的例子和下面不用初始化列表的构造函数看似没什么区别:Example::Example()

{

  ival = 0;

  dval = 0.0;

}

的确,这两个构造函数的结果是一样的。但区别在于:上面的构造函数(使用初始化列表的构造函数)显示的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显示的初始化。

初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。但有的时候必须用带有初始化列表的构造函数:

1)成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。

2const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

(8)       什么是默认构造函数
什么是无参构造函数
什么是带参构造函数
什么是拷贝构造函数
什么是赋值构造函数

(9)       派生类中构造函数对基类和子对象的调用问题:

http://hi.baidu.com/christianolove/blog/item/42ce98f7379a9328730eeca4.html

说明:派生类的对象创建需要先构造子类的对象,所以派生类的构造函数必须调用基类的构造函数,然后初始化派生类自己的成员,如果派生类中有子对象(内嵌对象,即另一个类的对象作为成员变量,那么有必要构造子对象,初始化子对象即可)。

(10)   拷贝构造函数(类对象的复制之一)

对于类的对象,可以复制。特别是可以用一个类的对象的复制对该类的其他对象进行初始化。这样就相当于完成构造函数的作用。完成此功能是通过调用类的拷贝构造函数完成的。默认情况下,编译器会给类生成一个拷贝构造函数。默认的拷贝构造函数完成的功能是对象的成员逐个的复制。对于数据成员都是值类型的时候,默认的拷贝构造函数足以完成功能,但是对于具有指针成员的类的时候,这样会与预料不一样的。这时候一般是自己实现拷贝构造函数,这也是c++经常提到的深浅拷贝的问题。拷贝构造函数的格式为:ClassName(const ClassName&);

拷贝构造函数的使用格式为:

ClassName a=ClassName(1,2); //调用构造函数

ClassName b(a);            //调用拷贝构造函数

ClassName c=a;     //仍然调用拷贝构造函数 (切记:这里不是调用赋值构造函数)

测试代码(以下类只包含值类型成员数据,默认的拷贝构造函数也可以完成其功能):

#include <iostream>

using namespace std;

 

 

class TTT

{

public:

   TTT(int a);

   TTT(const TTT& copy);

 

private:

   int data;

};

 

TTT::TTT(int a)

{

   data=a;

   cout<<"constructor called"<<endl;

}

 

TTT::TTT(const TTT& copy)

{

   data=copy.data;

   cout<<"copy constructor called"<<endl;

}

 

int main()

{

   TTT a(1);

   TTT b(a);

   TTT c=a;   //注意:不是调用赋值构造函数,仍然是调用拷贝构造函数

 

   return 0;

}

(11)   赋值构造函数(类对象的复制之二)

http://www.360doc.com/content/09/0502/15/137454_3345528.shtml(拷贝构造函数与赋值构造函数的异同)

赋值构造函数,个人觉得严格来说,并不能称为一种构造函数,构造函数是用来完成初始化的,而赋值构造函数是用于赋值的,就是初始化与赋值之间的区别。所以赋值构造函数更多的说法是赋值(复制)操作。就是对类的“=”运算符的处理。编译器同样会默认生成赋值构造函数(或者说默认定义了等于运算符),跟拷贝构造函数一样的规则,对于值类型和指针类型结果要注意。

对于有些情况,我们希望一个类能提供对其的引用和拷贝两种方式,一般是用拷贝构造函数实现拷贝功能,赋值构造函数提供引用的功能,当然,大部分情况下,赋值构造函数也是完整的数据拷贝,和拷贝构造函数的区别主要是赋值和初始化的区别。

赋值构造函数是等于运算符重载,格式为:TTT& operator=(const TTT&);//复制赋值

对于赋值构造函数实现的标准步骤要注意:1)防止自赋值(2)释放原来对象的内存分配(删除老元素)(3)初始化(4)赋值新元素

/*示例代码*/

#include <iostream>

using namespace std;

 

 

class TTT

{

public:

   TTT(char a,int size);

   TTT(const TTT& copy);

   TTT& operator=(const TTT& equal);

 

public:

   void printfData()

   {

            for(int i=0;i<sz;i++)

                     cout<<data[i]<<endl;

   }

 

   void modifyData(char b)

   {

            for(int i=0;i<sz;i++)

                     data[i]=b;

   }

private:

   char* data;

   int sz;

};

 

TTT::TTT(char a,int size)

{

   data=new char[size];

   sz=size;

   for(int i=0;i<size;i++)

            data[i]=a;

   cout<<"constructor called"<<endl;

}

 

TTT::TTT(const TTT& copy)

{

   sz=copy.sz;  //不要忘记这一点

   data=new char[copy.sz];

   for(int i=0;i<copy.sz;i++)

            data[i]=copy.data[i];

   cout<<"copy constructor called"<<endl;

}

 

TTT& TTT::operator=(const TTT& equal)

{

   if(this!=&equal)   //防止自赋值

   {

            delete[] data;       //释放

            sz=equal.sz; 

            data=new char[sz];    //初始化

            for(int i=0;i<sz;i++)         //拷贝

                     data[i]=equal.data[i];

   }

   return *this;

}

 

int main()

{

   TTT a('a',10);

   TTT b(a);

   b.printfData();

 

   TTT c('d',10);

   c=a;

   c.modifyData('b');

   c.printfData();          //如果采用默认的拷贝构造函数或赋值操作

   a.printfData();          //都会modifya的数据内容,不是我们希望的操作。

 

   return 0;

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值