构造与析构


构造函数

  • 类的一种特殊的成员函数
  • 它会在每次创建类的新对象时执行
  • 名字与类名相同,没有任何返回值
作用
  • 用于为某些成员变量设置初始值

默认构造

  • 没有参数的构造

  • 类存在有参构造函数时,编译器不会自动添加默认构造函数,这时想要编译器添加默认构造函数,解决:People() = default;

  • class People{
    private:
        string name;
        int age;
    public:
        // 默认构造
        People() = default; //让编译器自动添加默认构造函数
        // People() = delete; //该构造函数不能使用
        People(string newName) {
            name = newName;
            age = 18;
        }
        People(int newAge) {
            name = "People";
            age = newAge;
        }
        People(string newName, int newAge) {
            name = newName;
            age = newAge;
        }
    };
    
class People{
private:
    string name;
    int age;
public:
    // 默认构造
    People();
};

有参构造

  • 有参数的构造
#include 
#include 

using std::cout;
using std::endl;
using std::string;

class People{
private:
    string name;
    int age;
public:
    // 默认构造
    People() {
        name = "People";
        age = 18;
   }
   // 显性 explicit
   // 隐式 implicit
   explicit People(string newName) {
        name = newName;
        age = 18;
    }
    People(int newAge) {
        name = "People";
        age = newAge;
    }
    People(string newName, int newAge) {
        name = newName;
        age = newAge;
    }
};

int main() {

   return 0;

}

注意:

  • 不能存在参数类型顺序相同的构造函数
  • 即People(int a) 与 People(int b) 有且只能存在一个;
  • 只有一个参数时,编译器默认进行隐式转换

拷贝构造函数

  • 拷贝构造函数不写的话,编译器自动生成一个浅拷贝的拷贝构造函数
class People{
private:
    string name;
    int age;
public:
    // 默认构造
    People() {
        name = "People";
        age = 18;
    }
    // 有参构造函数
    People(string newName) {
        name = newName;
        age = 18;
    }
    People(int newAge) {
        name = "People";
        age = newAge;
    }
    People(string newName, int newAge) {
        name = newName;
        age = newAge;
    }
    // 拷贝构造
    People(const People& p) {
        name = p.name;
        age = p.age;
    }
};
为啥传const类型的引用?
  • 引用的C++底层原理为const类型的指针, 不能先定义后赋值;
  • 值传递会造成拷贝构造函数死循环。

参数初始化列表

意义与作用
  • 引用参数初始化列表, 没有进入函数体内部
  • 直接调用name的构造有参构造函数
  • 进入函数体内部前,初始化完成。
  • 写在函数体内部时
    • 第一步调用name的默认构造函数初始化;
    • 第二步调用string operator= 的操作符重载函数;

代码1测试

#include 

using namespace std;


class Point{


private:
    float x, y;



public:
    ~Point() {
        cout << "Point析构" << endl;
    }
    Point() : x(0), y(0) {
        cout << "Point默认构造" << endl;
    }
    Point(float xy) : x(xy), y(x) {
        cout << "Point一个参数的构造" << endl;
    }
    Point(float x, float y) : x(x), y(y) {
        cout << "Point两个参数的构造" << endl;
    }

    Point(const Point& p) : x(p.x), y(p.y) {
        cout << "Point拷贝构造" << endl;
    }

    Point& operator=(const Point& p) {
        cout << "赋值运算符重载";
        if (this != &p) {
            this->x = p.x;
            this->y = p.y;
            cout << ", ..., 赋值运算符重载成功!" << endl;
        }
        return *this;
    }

};


class Line{

private:
    Point p1, p2;

public:
    ~Line() {
        cout << "Line析构" << endl;
    }
    Line() : p1{0, 0}, p2{0, 0} {
        cout << "Line的默认构造" << endl;
    }
    Line(const Point& p) : p1(p), p2(p1) {
        cout << "Line的一个参数的构造" << endl;
    }

    // 发生两次Point的拷贝构造,一次Line的拷贝构造
    // Line(const Point& p1, const Point& p2) : p1(p1), p2(p2) {
    //     cout << "Line的两个参数的构造" << endl;
    // }

    Line(const Point& p1, const Point& p2) {
        cout << "Line的两个参数的构造开始" << endl;
        this->p1 = p1;
        this->p2 = p2;
        cout << "Line的两个参数的构造完成" << endl;
    }

    Line(const Line& l) : p1(l.p1), p2(l.p2) {
        cout << "Line拷贝构造" << endl;
    }

};


int main() {

    Point p1(1.1f, 2.3f), p2(1.2f, 2.3f);
    cout << "&&&&&&&&&&&&&&&&&&" << endl;
    Line l(p1, p2);



    cout << "&&&&&&&&&&&&&&&&&&" << endl;
    Line l1;

    return 0;
}

代码2测试

#include 

using namespace std;


class Point{


private:
    float x, y;



public:
    ~Point() {
        cout << "Point析构" << endl;
    }
    Point() : x(0), y(0) {
        cout << "Point默认构造" << endl;
    }
    Point(float xy) : x(xy), y(x) {
        cout << "Point一个参数的构造" << endl;
    }
    Point(float x, float y) : x(x), y(y) {
        cout << "Point两个参数的构造" << endl;
    }

    Point(const Point& p) : x(p.x), y(p.y) {
        cout << "Point拷贝构造" << endl;
    }

    Point& operator=(const Point& p) {
        cout << "赋值运算符重载";
        if (this != &p) {
            this->x = p.x;
            this->y = p.y;
            cout << ", ..., 赋值运算符重载成功!" << endl;
        }
        return *this;
    }

};


class Line{

private:
    Point p1, p2;

public:
    ~Line() {
        cout << "Line析构" << endl;
    }
    Line() : p1{0, 0}, p2{0, 0} {
        cout << "Line的默认构造" << endl;
    }
    Line(const Point& p) : p1(p), p2(p1) {
        cout << "Line的一个参数的构造" << endl;
    }

    // 发生两次Point的拷贝构造,一次Line的拷贝构造
    Line(const Point& p1, const Point& p2) : p1(p1), p2(p2) {
        cout << "Line的两个参数的构造" << endl;
    }

    // Line(const Point& p1, const Point& p2) {
    //     cout << "Line的两个参数的构造开始" << endl;
    //     this->p1 = p1;
    //     this->p2 = p2;
    //     cout << "Line的两个参数的构造完成" << endl;
    // }

    Line(const Line& l) : p1(l.p1), p2(l.p2) {
        cout << "Line拷贝构造" << endl;
    }

};


int main() {

    Point p1(1.1f, 2.3f), p2(1.2f, 2.3f);
    cout << "&&&&&&&&&&&&&&&&&&" << endl;
    Line l(p1, p2);



    cout << "&&&&&&&&&&&&&&&&&&" << endl;
    Line l1;

    return 0;
}

测试结果

1、代码1测试结果

Point两个参数的构造
Point两个参数的构造
&&&&&&&&&&&&&&&&&&
Point默认构造
Point默认构造
Line的两个参数的构造开始
赋值运算符重载, ..., 赋值运算符重载成功!
赋值运算符重载, ..., 赋值运算符重载成功!
Line的两个参数的构造完成
&&&&&&&&&&&&&&&&&&
Point两个参数的构造
Point两个参数的构造
Line的默认构造
Line析构
Point析构
Point析构
Line析构
Point析构
Point析构
Point析构
Point析构


2、代码二测试结果

Point两个参数的构造
Point两个参数的构造
&&&&&&&&&&&&&&&&&&
Point拷贝构造
Point拷贝构造
Line的两个参数的构造
&&&&&&&&&&&&&&&&&&
Point两个参数的构造
Point两个参数的构造
Line的默认构造
Line析构
Point析构
Point析构
Line析构
Point析构
Point析构
Point析构
Point析构

分析

  • 可以看出来,结果二比结果一少了赋值运操作
#include 
#include 

using namespace std;

class People{
    friend int main();
    //用友员函数不用成员函数的原因:
    /*
    1、cout是ostream类的对象
    2、这个函数的第一个对象不是当前类的对象,而是ostream类的
    */
    friend ostream& operator<<(ostream& os, const People&);

private:
    string name;
    int age;

public:

    // 默认构造
    People() : name("People"), age(18) {
        // 引用参数初始化列表, 没有进入函数体内部
        // 直接调用name的构造有参构造函数
        // 进入函数体内部前,初始化完成。

        // 写在函数体内部:
        name = "People";
        // 第一步调用name的默认构造函数,
        // 第二部调用string operator= 的操作符重载函数
        // age = 18;
    }

    // 有参构造
    People(string s) : name(s), age(18) {
        cout << "string constructor" << endl;
    }
    // implicit 隐式转换
    // explicit 显式构造
    // 编译器默认隐式转换,即为implicit
    explicit People (int age) : name("People"), age(age) {
        cout << "int constructor" << endl;
    }

    // 拷贝构造函数
    People(const People& p) : name(p.name), age(p.age) {
        // 引用的C++底层原理为const类型的指针, 不能先定义后赋值;
        cout << "copy constructor" << endl;
        // 值传递会造成拷贝构造函数死循环
    }

    ~People() {
        cout << "destructor" << endl;
    } 

    // 赋值运算符重载
    People& operator= (const People& p) {
        name = p.name;
        age = p.age;
        cout << "call operator=" << endl;
        return *this;
    }
    // 法一
    // 使用方法:p << cout;
    // 原因:当前函数的第一个参数是该类的对象
    // ostream& operator<< (ostream& os) {
    //     os << "name = " << name << ", age = " << age << endl;
    //     return os;
    // }

    // 法二设置为友员函数

};

ostream& operator<< (ostream& os, const People& p) {
    os << "name = " << p.name << ", age = " << p.age << endl;
    return os;
}


int main() {

    People p("Axieyun");
    cout << "name = " << p.name << ", age = " << p.age << endl;

    People p1(p);
    cout << p;
    cout << "p.arrces = " << &p << ", p1.arrces = " << &p1 << endl;

    // 赋值构造
    People p2 = People(18); // 隐式转换为People对象,再拷贝构造给p2;
    p2 = p;
    // p << cout;
    cout << p;
    return 0;
}

析构

  • 默认构造函数前加~
class People{
private:
    string name;
    int age;
public:
    ~People() {
        cout << "destructor" << endl;
        // delete操作
    };
};

35法则

  • 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数

  • 需要拷贝操作的类也需要赋值操作,反之亦然

  • 析构函数不能删除

  • 如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的

  • 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作,即

    • 不能进行以下操作

      int b = 1;
      int& a;
      a = b;
      
    • 应该这样写

    • int b = 1;
      int& a = b;
      
    • const类型同理。


文章作者: Axieyun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Axieyun !
评论
评论
  目录