六、构造函数、复制构造函数 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; }

 

posted @ 2022-05-14 15:44  Grit_L。  阅读(124)  评论(0编辑  收藏  举报