(c/c++)——构造函数和析构函数的注意事项


1.默认参数(缺省值)

1、在类中,构造函数的声明对形参指定了默认参数,类外的定义不能再指定默认参数了

class Clock
{
private:
    int hour, minute, second;
public:
    Clock(int h = 0, int m = 0, int s = 0);
};
Clock::Clock(int h=0 , int m=0 , int s=0){}  // 错误
Clock::Clock(int h , int m , int s){}  

2、当自己写了一个有参构造函数后,编译器将不会再自动提供无参构造,这时创建一个无参的类对象就会报错
解决方法:给有参构造函数加上缺省值

class Clock
{
private:
    int hour, minute, second;
public:
    Clock(int h = 0, int m = 0, int s = 0);
};
Clock::Clock(int h=0 , int m=0 , int s=0){}  // 错误
Clock::Clock(int h , int m , int s){}  

int main()
{
	Clock c1;  // 有参构造加上缺省值后可以创建
	Clock c2(7,8,9);
}

2.初始化列表

初始化列表一般只能在构造函数中使用。在类中使用初始化列表,对性能有所提升(用初始化列表调用的是成员类的拷贝构造函数,而赋值则是先创建成员类的对象(将调用成员类的普通构造函数),然后再赋值)。

注:如果成员是常量和引用,必须使用初始列表,因为常量和引用只能在定义的时候初始化

class A               
{
public:
    string m_xm;                    
    A()                                  
    { m_xm.clear();  cout << "调用了A()构造函数。\n"; }
    A(string xm)                 
    { m_xm = xm;  cout << "调用了A(string xm)构造函数。\n"; }
    A(const A& aa)      
    { m_xm = aa.m_xm;  cout << "调用了A(const CBoy &aa)拷贝构造函数。\n"; }
};

class C                 
{
public:
    string    m_name;   
    // 常量和引用必须使用初始化列表                  
    const int  m_age;                     
    A&   m_A;                         

    //C(string name, int age,CBoy &boy)     
    //{
    //    m_name = name; m_age = age; m_boy.m_xm = boy.m_xm;  // 错误
    //    cout << "调用了CGirl(name,age,boy)构造函数。\n";
    //}

    C(string name, int age, A& a) :m_name(name), m_age(age),m_A(a)     
    {
        cout << "调用了C(name,age,m_A)构造函数。\n";
    }
    
    void show() { cout << "姓名:" << m_name << ",年龄:" << m_age << ",链接:" << m_A.m_xm << endl; }
};

int main()
{
    A a("掌声");
    C c1("冰冰",18,a);
    c1.show();
}

3.构造和析构的调用顺序

  • 先构造基类(最远的基类最先构造,如(父——子——孙)的关系,先调用父的构造)
  • 再构造成员(成员构造顺序和声明顺序符合)
  • 最后构造自身(调用构造函数)
  • 析构顺序与构造顺序相反
#include <iostream>
using namespace std;
class A {
public:
  A() { cout << "Constructing A" << endl; }
  ~A() { cout << "Destructing A" << endl; }
};
class B {
public:
  B() { cout << "Constructing B" << endl; }
  ~B() { cout << "Destructing B" << endl; }
};
class C {
public:
  C() { cout << "Constructing C" << endl; }
  ~C() { cout << "Destructing C" << endl; }
};
class D : public C {
public:
  D() { cout << "Constructing D" << endl; }
  ~D() { cout << "Destructing D" << endl; }
};
class E : public D{
public:
  E() { cout << "Constructing E" << endl; }
  ~E() { cout << "Destructing E" << endl; }
  B b;
  A a;
  C c;
  D d;
};

int main() {
  E e; 
}

/*
结果:
先构造基类
Constructing C
Constructing D
再按顺手构造成员
Constructing B
Constructing A
Constructing C
成员d中存在继承,先构造基类c
Constructing C
Constructing D
最后构造自身
Constructing E

Destructing E
Destructing D
Destructing C
Destructing C
Destructing A
Destructing B
Destructing D
Destructing C
*/

4.派生类构造函数的注意事项

1、派生类只能采用构造函数初始化列表的方式向基类或成员对象的构造函数传递参数

派生类构造函数名(参数表):基类构造函数名(参数表),成员对象名1(参数表),…
{
    //……
}
class A {
  int x;

public:
  A(int i) {
    x = i;
    cout << "A constructor x=" << x << endl;
  }
  ~A() { cout << "A destructor.." << endl; }
};

class Base {
private:
  int x;

public:
  Base(int a) 
  {
    x = a;
    cout << "Base constructor x=" << x << endl;
  }
  ~Base() { cout << "Base destructor..." << endl; }
};

class Derived : public Base {
private:
  int m_y;
  A m_a;

public:
  Derived(int a, int y, int i) : Base(a),m_a(i) //派生类构造函数的初始化列表
  { 
    // Base(a); 错误
    m_y = y;
    // m_a = i; 错误
    cout << "Derived constructor m_y=" << m_y << endl;
  }
  ~Derived() { cout << "Derived destructor..." << endl; }
};
int main() {
  Derived d(1, 2, 3);
  
  return 0;
}

2、但是构造函数的调用顺序是按照前面(3)的规则,与初始化列表中的次序无关

#include <iostream>
using namespace std;

class A {
  int x;
public:
  A(int i) {
    x = i;
    cout << "A constructor x=" << x << endl;
  }
  ~A() { cout << "A destructor.." << endl; }
};

class B {
  int y;
public:
  B(int i) {
    y = i;
    cout << "B constructor y=" << y << endl;
  }
  ~B() { cout << "B destructor.." << endl; }
};

class Base {
private:
  int x;

public:
  Base(int a) 
  {
    x = a;
    cout << "Base constructor x=" << x << endl;
  }
  ~Base() { cout << "Base destructor..." << endl; }
};

class Derived : public Base {
private:
  int m_y;
  B m_b;
  A m_a;

public:
  Derived(int a, int b, int y, int i) : m_a(a),m_b(b),Base(i)//派生类构造函数的初始化列表,这里的次序不能决定构造函数的调用顺序
  { 
    // m_a(a); 错误
    // m_b(b); 错误
    m_y = y;
    // Base(i); 错误
    cout << "Derived constructor m_y=" << m_y << endl;
  }
  ~Derived() { cout << "Derived destructor..." << endl; }
};
int main() {
  Derived d(1, 2, 3, 4);
}
/*
输出:
Base constructor x=4
B constructor y=2
A constructor x=1
Derived constructor m_y=3
Derived destructor...
A destructor..
B destructor..
Base destructor...
*/

a)派生类可以不定义构造函数的情况:

  1. 基类没有定义任何构造函数。
  2. 基类具有缺省参数的构造函数
  3. 基类具有无参构造函数

(b)派生类必须定义构造函数的情况:
  当基类或成员对象所属类只含有带参数的构造函数时,即使派生类本身没有数据成员要初始化,它也必须定义构造函数,并以构造函数初始化列表的方式向基类和成员对象的构造函数传递参数,以实现基类子对象和成员对象的初始化。

(c)派生类的构造函数只负责直接基类的初始化(但有个例外:当派生类存在虚基类时,所有虚基类都由最后的派生类负责初始化)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想要躺平的一枚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值