六、构造函数、复制构造函数 copy constructor、类型转换构造函数
1、构造函数
1)基本概念
- 成员函数的一种
- 名字与类名相同,可以有参数,不能有返回值(void也不行)
- 作用是对对象进行初始化,如给成员变量赋初值
- 如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数 --默认构造函数无参数,不做任何操作
- 如果定义了构造函数,则编译器不生成默认的无参数的构造函数
- 对象生成时构造函数自动被调用。对象一旦生成,就再也不能在 其上执行构造函数
- 一个类可以有多个构造函数
2)为什么需要构造函数?
构造函数执行必要的初始化工作,有了构造函数,就不 必专门再写初始化函数,也不用担心忘记调用初始化函数。
有时对象没被初始化就使用,会导致程序出错。
class Complex { private : double real, imag; public: void Set( double r, double i); }; //编译器自动生成默认构造函数 Complex c1; //默认构造函数被调用 Complex * pc = new Complex; //默认构造函数被调用
class Complex { private : double real, imag; public: Complex( double r, double i = 0); }; Complex::Complex( double r, double i) { real = r; imag = i; } Complex c1; // error, 缺少构造函数的参 数 Complex * pc = new Complex; // error, 没有参数 Complex c1(2); // OK Complex c1(2,4), c2(3,5); Complex * pc = new Complex(3,4);
可以有多个构造函数参数个数和类型不同
class Complex { private : double real, imag; public: void Set( double r, double i ); Complex(double r, double i ); Complex (double r ); Complex (Complex c1, Complex c2); };
Complex::Complex(double r, double i) { real = r; imag = i; }
Complex::Complex(double r) { real = r; imag = 0; } Complex::Complex (Complex c1, Complex c2); { real = c1.real+c2.real; imag = c1.imag+c2.imag; }
Complex c1(3) , c2 (1,0), c3(c1,c2); // c1 = {3, 0}, c2 = {1, 0}, c3 = {4, 0};
构造函数最好是public的,private构造函数 不能直接用来初始化对象
class CSample{ private: CSample() { } }; int main(){ CSample Obj; //err. 唯一构造函数是private return 0; }
3)构造函数在数组中的使用
例子1:
class CSample { int x; public: CSample() { cout << "Constructor 1 Called" << endl; } CSample(int n) { x = n; cout << "Constructor 2 Called" << endl; } }; int main(){ CSample array1[2]; cout << "step1"<<endl; CSample array2[2] = {4,5}; cout << "step2"<<endl; CSample array3[2] = {3}; cout << "step3"<<endl; CSample * array4 = new CSample[2]; delete []array4; return 0; } 输出: Constructor 1Called Constructor 1Called step1 Constructor 2 Called Constructor 2 Called step2 Constructor 2 Called Constructor 1 Called step3 Constructor 1 Called Constructor 1 Called
例子二:
class Test { public: Test( int n) { } //(1) Test( int n, int m) { } //(2) Test() { } //(3) }; Test array1[3] = { 1, Test(1,2) }; // 三个元素分别用(1),(2),(3)初始化 Test array2[3] = { Test(2,3), Test(1,2) , 1}; // 三个元素分别用(2),(2),(1)初始化 Test * pArray[3] = { new Test(4), new Test(1,2) }; //两个元素分别用(1),(2) 初始化
二、 赋值构造函数
1)基本概念
只有一个参数,即对同类对象的引用。
形如 X::X( X& )或X::X(const X &), 二者选一 后者能以常量对象作为参数
如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能。
class Complex { private : double real,imag; }; Complex c1; //调用缺省无参构造函数 Complex c2(c1);//调用缺省的复制构造函数,将 c2 初始化成和c1一样
如果定义的自己的复制构造函数, 则默认的复制构造函数不存在。
class Complex { public : double real,imag; Complex(){ } Complex( const Complex & c ) { real = c.real; imag = c.imag; cout << “Copy Constructor called”; } }; Complex c1; Complex c2(c1);//调用自己定义的复制构造函数,输出 Copy Constructor called
不允许有形如 X::X( X )的构造函数。
class CSample { CSample( CSample c ) { } //错,不允许这样的构造函数 };
2)复制构造函数起作用的三种情况
- 当用一个对象去初始化同类的另一个对象时。
Complex c2(c1);
Complex c2 = c1; //初始化语句,非赋值语句
- 如果某函数有一个参数是类 A 的对象,那么该函数被调用时,类A的复制构造函数将被调用。
class A { public: A() { }; A( A & a) { cout << "Copy constructor called" <<endl; } } void Func(A a1){ } int main(){ A a2; Func(a2); return 0; }
- 如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用:
class A { public: int v; A(int n) { v = n; }; A( const A & a) { v = a.v; cout << "Copy constructor called" <<endl; } }; A Func() { A b(4); return b; } int main() { cout << Func().v << endl; return 0; }
输出结果:
Copy constructor called
4
注意:对象间赋值并不导致复制构造函数被调用
class CMyclass { public: int n; CMyclass() {}; CMyclass( CMyclass & c) { n = 2 * c.n ; } }; int main() { CMyclass c1,c2; c1.n = 5; c2 = c1; CMyclass c3(c1); cout <<"c2.n=" << c2.n << ","; cout <<"c3.n=" << c3.n << endl; return 0; } 输出: c2.n=5,c3.n=10
- 3)常量引用参数的使用
void fun(CMyclass obj_ ) {
cout << "fun" << endl;
}
这样的函数,调用时生成形参会引发复制构造函数调用,开销比较大。
所以可以考虑使用 CMyclass & 引用类型作为参数。
如果希望确保实参的值在函数中不应被改变,那么可以加上const 关键字:
void fun(const CMyclass & obj) {
//函数中任何试图改变 obj值的语句都将是变成非法
}
3、类型转换构造函数
1)基本概念
--定义转换构造函数的目的是实现类型的自动转换。
--只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数。
--当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量)。
实例1:
class Complex { public: double real, imag; Complex( int i) {//类型转换构造函数 cout << "IntConstructor called" << endl; real = i; imag = 0; } Complex(double r,double i) {
real = r;
imag = i;
} }; int main () { Complex c1(7,8); Complex c2 = 12; c1 = 9; // 9被自动转换成一个临时Complex对象 cout << c1.real << "," << c1.imag << endl; return 0; }
实例2:
class Complex { public: double real, imag; explicit Complex( int i) {//显式类型转换构造函数 cout << "IntConstructor called" << endl; real = i; imag = 0; } Complex(double r,double i) {
real = r;
imag = i;
} }; int main () { Complex c1(7,8); Complex c2 = Complex(12); c1 = 9; // error, 9不能被自动转换成一个临时Complex对象,临时对象赋值给c1 c1 = Complex(9) //ok cout << c1.real << "," << c1.imag << endl; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏