C++

C++语法系列之10--构造函数总结

2018-04-15  本文已影响0人  hello12qwerz

1 不提供显示的构造函数

class Counter {
  private:
     int cnt;
 public getCnt() {
     return cnt;
 }
  
}

Counter myCnt;//在堆栈中,此时会自动调用默认构造函数初始化myCnt
myCnt.getCnt();//0

此时编译器会默认为类生成一个默认构造函数(不带参数的构造函数,什么也不做)。上例中打印结果为0(类中定义的成员变量,基本类型会初始化为0)。
编译器提供的默认构造函数相当于:

Counter() {
  //空实现
}

2 显示的提供构造函数

class Counter {
private:
    int mA;
    float mB;
public:
    Counter(int a);
    Counter(int a, float b);
    
};
Counter::Counter(int a) {
    mA = a;
}
Counter::Counter(int a, float b) {
    mA = a;
    mB = b;
}

int main(int argc, const char * argv[]) {
    //Counter cnt;//error, cannot compile
    Counter myCnt(1);
    Counter myCnt2(1,3.0);

}

上面的例子提供了2个构造函数(也即重载构造函数)。

3 复制构造函数

复制构造函数的参数为对象的引用


class Counter {
private:
    int mA;
    float mB;
public:
    Counter(int a);
    Counter(int a, float b);
    Counter(const Counter& c);
    
    int getMA() const;
};
Counter::Counter(int a) {
    mA = a;
}
Counter::Counter(int a, float b) {
    mA = a;
    mB = b;
}

Counter::Counter(const Counter& c) {
    mA = c.mA;
    mB = c.mB;
}

int Counter::getMA() const {
    return mA;
}

int main(int argc, const char * argv[]) {
    Counter myCnt(1,3.0);
    Counter myCopyCnt(myCnt);
    cout << myCopyCnt.getMA() << endl;
}

注意:

  1. 复制构造函数只在初始化的时候才会调用。如果是赋值而不是初始化,那么调用的是赋值运算符,而不是复制构造函数。
  2. 浅拷贝/深拷贝:复制构造函数和赋值运算符,如果有指针数据成员,涉及到内存分类的场景的话,需要考虑浅拷贝/深拷贝。浅拷贝只是将引用或者指针指向对应的成员变量。而深拷贝则会分配内存,然后将引用指向对应的内存。

4 类型转换构造函数


class Complex {
private:
    float mReal;
    float mImg;
public:
    Complex();
    Complex(float real);
    Complex(const Complex& c);
    
    Complex operator+(const Complex& c);

    float getReal() const;
    float getImg() const;
    void setReal(float real);
    void setImg(float img);
};

Complex::Complex() {
    mReal = 0.0;
    mImg = 0.0;
}

Complex::Complex(const Complex& c) {
    mReal = c.mReal;
    mImg = c.mImg;
}

Complex::Complex(float real) {
    mReal = real;
    mImg = 0;
}

float Complex::getReal() const {
    return mReal;
}

float Complex::getImg() const {
    return mImg;
}

Complex Complex::operator+(const Complex& c) {
    Complex complex;
    complex.setReal(mReal + c.getReal());
    complex.setImg(mImg + c.getImg());
    return complex;
}

int main(int argc, const char * argv[]) {
    Complex complex;
    Complex c1 = 1.0f;
    cout << "real:" << c1.getReal() << ";img:" << c1.getImg() << endl;
   # real: 1.0; img:0.0

  Complex a(2.0);
  Complex b = a + 1.0;//real: 3.0
}
  1. 上面的例子中,Complex c1= 1.0f; 会调用Complex的构造函数Complex(float real),也就是类型转换.
  2. Complex b = a + 1.0 此时提供了operator+,也会隐式的调用构造函数进行类型转换。但是Complex c = 1.0 + a;无法编译通过,因为Complex的operator+运算符不具有交换性,如果要可以编译通过,必须提供全局的operator+ 才可以。

5 禁止隐式调用构造函数进行类型转换

有些时候,上面的效果是我们期望的。但是有些时候,如果不是我们需要的(比如错误的写了Complex c1 = 1.0f),如何禁止这种类型转换呢?
可以使用explicit关键字解决:

class Complex {
private:
    float mReal;
    float mImg;
public:
    Complex();
    explicit Complex(float a);
    Complex(const Complex& c);
    
    Complex operator+(const Complex& c);

    float getReal() const;
    float getImg() const;
    void setReal(float real);
    void setImg(float img);
};

Complex::Complex() {
    mReal = 0.0;
    mImg = 0.0;
}

Complex::Complex(const Complex& c) {
    mReal = c.mReal;
    mImg = c.mImg;
}

Complex::Complex(float a) {
    mReal = a;
    mImg = 0;
}

float Complex::getReal() const {
    return mReal;
}

float Complex::getImg() const {
    return mImg;
}

void Complex::setImg(float img) {
    mImg = img;
}

void Complex::setReal(float real) {
    mReal = real;
}

int main(int argc, const char * argv[]) {
    Complex complex;
    Complex c1 = 1.0f; //error,此时无法将float类型转换为Complex
    Complex c2(1.0f);   //此时必须这样显示的调用构造函数

注意:
1)explicit用于用于只有一个参数的构造函数才有效;
2)假如构造函数有两个参数,explicit无效;
3)假如构造函数有2个或者多个参数,但是除了第一个参数外,其余均带有默认值时,explicit有效;

6 赋值运算符

//为了支持连等操作(形如这样: a =b  =c ),返回值必须为引用
Complex &operator=(const Complex &rhs)
   {
       if ( this == &rhs ) 
       {
           return *this;
       }
            
       // 复制等号右边的成员到左边的对象中
       this->mReal = rhs.mReal;
       this->mImg = rhs.mImg;
       return *this;
   }

以上是一个赋值运算符的例子,没有涉及到内存分配。所以看不出来深拷贝和浅拷贝的区别。如果有引用和指针,涉及到内存分配,那么需要使用深拷贝。

7 带有默认值的构造函数

class Base {
public:
    Base();
    Base(int a = 0);
    
private:
    int mA;
};
Base::Base() {
}

Base::Base(int a) {
    mA = a;
}

int main(int argc, const char * argv[]) {
    Base b;//Error:  Call to constructor of "Base" is ambiguous
    return 0;
}

以上代码定义了两个构造函数:无参构造函数和带有默认值得构造函数。由于无法这两个构造函数,编译会出错。
所以:定义构造函数时,不允许定义导致歧义的构造函数。

8 继承层次的构造函数默认值


class Base {
public:
    Base(int a = 0);
    virtual ~Base();
    
private:
    int mA;
};


Base::Base(int a) {
    mA = a;
    cout << "Base a = " << mA << endl;
}

Base::~Base() {
    
}

class Derived : public Base {
public:
    Derived(int a = 1);
    virtual ~Derived();
};

Derived::Derived(int a) {
    cout <<"Derived a = " << a << endl;
}

Derived::~Derived() {
    
}

int main(int argc, const char * argv[]) {
    Derived d;
    cout << endl << endl;
    Base* b = new Derived();
    
    return 0;
}

运行结果如下:

Base a = 0
Derived a = 1


Base a = 0
Derived a = 1

说明:C++中默认参数不会继承。C++根据描述对象的表达式类型绑定默认参数,而不是根据实际的对象类型绑定参数。

上一篇 下一篇

猜你喜欢

热点阅读