C++类的六个特殊成员函数

有些话、适合烂在心里 提交于 2020-02-24 21:07:36

1.设置六个函数的参数,先让函数运行起来

//
// Created by liuhao on 20-1-1.
//


//=================================================================================
#include <iostream>

//using namespace std;

class Stu {

private:
    std::string name = "无名氏 ";
    int age = 0;
    int *d = nullptr;
public:
    Stu() { std::cout << name << age << " 执行了无参构造函数!" << std::endl; };

    Stu(int a) { std::cout << name << age << " 执行了有参构造函数!" << std::endl; };

    Stu(const Stu &s) { std::cout << name << age << " 执行了拷贝构造函数!" << std::endl; };

    Stu &operator=(const Stu &s) { std::cout << name << age << " 执行了拷贝赋值运算符函数!" << std::endl; };

    Stu(Stu &&s) { std::cout << name << age << " 执行了移动构造函数!" << std::endl; };

    Stu &operator=(Stu &&s) { std::cout << name << age << " 执行了移动赋值运算符函数!" << std::endl; };

    ~Stu() { std::cout << name << age << " 执行了析构函数!" << std::endl; };

};


int main() {

    Stu s1;

    Stu s2(100);

    Stu s3 = s2;

    Stu s4;
    s4 = s3;

    Stu s5 = std::move(s4);

    Stu s6;
    s6 = std::move(s5);

    return 0;
}
//=================================================================================


1.输出结果

/home/liuhao/CLionProjects/Robot_modules/build/debug/bin/test/class_function
无名氏 0 执行了无参构造函数!
无名氏 0 执行了有参构造函数!
无名氏 0 执行了拷贝构造函数!
无名氏 0 执行了无参构造函数!
无名氏 0 执行了拷贝赋值运算符函数!
无名氏 0 执行了移动构造函数!
无名氏 0 执行了无参构造函数!
无名氏 0 执行了移动赋值运算符函数!
无名氏 0 执行了析构函数!
无名氏 0 执行了析构函数!
无名氏 0 执行了析构函数!
无名氏 0 执行了析构函数!
无名氏 0 执行了析构函数!
无名氏 0 执行了析构函数!

Process finished with exit code 0

2.六个函数的具体功能实现

//
// Created by liuhao on 20-1-1.
//


////=================================================================================
#include <iostream>

//using namespace std;


class AA {

private:
    std::string name = "";
    int age = 0;
    int *d = nullptr;

public:
    explicit AA(std::string name) {      // 单一参数的构造函数最好加上explicit,避免隐式转换
        this->name = name;
        age = 123;
        d = new int(456);        // 这里要给定指向,因为后面有*AA_d

        std::cout << this->name << age << " 执行了无参构造函数!" << std::endl;
        std::cout << this->name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << this->name << age << " 指针指向的地址的内容:" << *d << std::endl;
        std::cout << this->name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;
    };

    ~AA() {
        delete d;
        d = nullptr;

        std::cout << name << age << " 执行了析构函数!" << std::endl;
    };
};






class Stu {

public:
    std::string name = "无名氏 ";
    int age = 0;
    int *d = nullptr;
    AA *p_internal = nullptr;   // 指向内部生成的内部对象,要在析构函数里面先释放后置空
    AA *p_external = nullptr;   // 指向外部传递的外部对象,不用在析构函数里面管理,

//=================================================================================
public:
    Stu() {
        name = "s1 ";
        age = 1;
        // TODO s1是空指针,虽然没有new,但是析构函数可以用delete
        std::cout << name << age << " 执行了无参构造函数!" << std::endl;
        std::cout << name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << name << age << " 指针指向的地址的内容:" << "空指针没有指向,不能解引用操作" << std::endl;
        std::cout << name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;

    };

    Stu(int i, std::string name, AA *p_temp) {
        this->name = "s2 ";
        age = 2;

        d = new int(i);

        std::cout << this->name << age << " 执行了有参构造函数!" << std::endl;
        std::cout << this->name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << this->name << age << " 指针指向的地址的内容:" << *d << std::endl;
        std::cout << this->name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;

        p_internal = new AA(name);

        if (p_temp == nullptr) {
            p_external = nullptr;
        } else {
            p_external = p_temp;
            p_temp = nullptr;               // 这个有必要吗?
        }
    };

//=================================================================================
    Stu(const Stu &s) {
        name = "s3 ";
        age = 3;

        if (s.d == nullptr) {
            d = nullptr;
        } else {
            //开辟新的空间
            d = new int();      // 不要使用浅拷贝(存在动态成员的隐患),尽量使用深拷贝
            //把原来的值,拷贝过来。
            *d = *s.d;          // TODO 注意s不能是空指针,比如s1
        }

        std::cout << name << age << " 执行了拷贝构造函数(深拷贝)!" << std::endl;
        std::cout << name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << name << age << " 指针指向的地址的内容:" << *d << std::endl;
        std::cout << name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;
    };

//=================================================================================
    Stu(Stu &&s) noexcept {     // noexcept表明函数或操作不会发生异常,会给编译器更大的优化空间。
        name = "s4 ";
        age = 4;                // 移动赋值运算符函数,那这个s.name和s.age怎么处理的啊

        if (s.d == nullptr) {
            d = nullptr;
        } else {
            // s的指向的地址赋值给当前对象this
            d = s.d;
            // s需要置空,断开指向,避免2个指针指向同一块空间
            s.d = nullptr;
        }
        std::cout << name << age << " 执行了移动构造函数!" << std::endl;
        std::cout << name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << name << age << " 指针指向的地址的内容:" << *d << std::endl;
        std::cout << name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;
    };

//=================================================================================
    Stu &operator=(const Stu &s) {
        name = "s5 ";
        age = 5;

        if (s.d == nullptr) {
            d = nullptr;
        } else {
            //开辟新的空间
            d = new int();      // 不要使用浅拷贝(存在动态成员的隐患),尽量使用深拷贝
            //把原来的值,拷贝过来。
            *d = *s.d;          // TODO 注意s不能是空指针,比如s1
        }
        std::cout << name << age << " 执行了拷贝赋值运算符函数!" << std::endl;
        std::cout << name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << name << age << " 指针指向的地址的内容:" << *d << std::endl;
        std::cout << name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;

        return *this;
    };

//=================================================================================
    Stu &operator=(Stu &&s) noexcept {     // noexcept表明函数或操作不会发生异常,会给编译器更大的优化空间。
        name = "s6 ";
        age = 6;                // 移动赋值运算符函数,那这个s.name和s.age怎么处理的啊

        if (s.d == nullptr) {
            d = nullptr;
        } else {
            // s的指向的地址赋值给当前对象this
            d = s.d;
            // s需要置空,断开指向,避免2个指针指向同一块空间
            s.d = nullptr;
        }
        std::cout << name << age << " 执行了移动赋值运算符函数!" << std::endl;
        std::cout << name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << name << age << " 指针指向的地址的内容:" << *d << std::endl;
        std::cout << name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;

        return *this;
    };

//=================================================================================
    ~Stu() {
        delete d;
        d = nullptr;

        std::cout << name << age << " 执行了析构函数!" << std::endl;

        delete p_internal;
        p_internal = nullptr;
    };
//=================================================================================




//=================================================================================
    Stu &operator&&(const Stu &s) {     // 重新定义了&&运算符(DIY)
        name = "s9 ";
        age = 9;

        if (s.d == nullptr) {
            d = nullptr;
        } else {
            //开辟新的空间
            d = new int();      // 不要使用浅拷贝(存在动态成员的隐患),尽量使用深拷贝
            //把原来的值,拷贝过来。
            *d = *s.d;          // TODO 注意s不能是空指针,比如s1
        }
        std::cout << name << age << " 执行了拷贝赋值运算符函数!" << std::endl;
        std::cout << name << age << " 指针指向的地址:" << d << std::endl;
        std::cout << name << age << " 指针指向的地址的内容:" << *d << std::endl;
        std::cout << name << age << " 指针自身的地址:" << &d << std::endl;
        std::cout << "----------------------------------" << std::endl;

        return *this;
    };
//=================================================================================

};





//=================================================================================

int main() {

// 类的六个特殊成员函数总结如下:
// 普通构造函数(无参构造函数:Stu(int i)、有参构造函数:Stu(int i))   析构函数:~Stu()
// 拷贝构造函数:Stu(const Stu &s)                                 拷贝赋值运算符函数:Stu(Stu &&s)
// 移动构造函数:Stu &operator=(const Stu &s)                      移动赋值运算符函数:Stu &operator=(Stu &&s)

//=================================================================================
    AA aa("外部对象 ");
//=================================================================================
    Stu s1;                         // 无参构造函数
    Stu s2(100, "内部对象 ", &aa);   // 有参构造函数
//=================================================================================
    Stu s3 = s2;                    // 拷贝构造函数(深拷贝)        // 不能用s1,会有内存问题,看上面;
//=================================================================================
    Stu s4;
    s4 = s3;                        // 拷贝赋值运算符函数          // 不能用s1,会有内存问题,看上面;
//=================================================================================
    Stu s5 = std::move(s4);              // 移动构造函数               // 不能用s1,会有内存问题,看上面;
    // TODO s3竟然还可以再用
    std::cout << "**************************************** " << s4.d << std::endl;      // 0
    std::cout << "**************************************** " << s4.name << std::endl;   // s4
    std::cout << "**************************************** " << s4.age << std::endl;    // 4
//=================================================================================
    Stu s6;
    s6 = std::move(s5);                  // 移动赋值运算符函数        // 不能用s1,会有内存问题,看上面;
    // TODO s5竟然还可以再用
    std::cout << "**************************************** " << s5.d << std::endl;      // 0
    std::cout << "**************************************** " << s5.name << std::endl;   // s5
    std::cout << "**************************************** " << s5.age << std::endl;    // 5
//=================================================================================
    Stu s9;
    s9 && s6;                       // 重新定义了&&运算符(DIY)

    s9 && s6 && s3 && s2;           // 运算符多次重载
//    s9 && s6 && s3 && s2 && s1;   // s1里面有空指针,不能参与运算符多次重载,因为operator&&函数里面有解引用操作
//=================================================================================
    return 0;
}


2.输出结果

/home/liuhao/CLionProjects/Robot_modules/build/debug/bin/test/class_function
外部对象 123 执行了无参构造函数!
外部对象 123 指针指向的地址:0x10e7c20
外部对象 123 指针指向的地址的内容:456
外部对象 123 指针自身的地址:0x7ffdaabbeab8
----------------------------------
s1 1 执行了无参构造函数!
s1 1 指针指向的地址:0
s1 1 指针指向的地址的内容:空指针没有指向,不能解引用操作
s1 1 指针自身的地址:0x7ffdaabbeae8
----------------------------------
s2 2 执行了有参构造函数!
s2 2 指针指向的地址:0x10e8050
s2 2 指针指向的地址的内容:100
s2 2 指针自身的地址:0x7ffdaabbeb28
----------------------------------
内部对象 123 执行了无参构造函数!
内部对象 123 指针指向的地址:0x10e80b0
内部对象 123 指针指向的地址的内容:456
内部对象 123 指针自身的地址:0x10e8098
----------------------------------
s3 3 执行了拷贝构造函数(深拷贝)!
s3 3 指针指向的地址:0x10e80d0
s3 3 指针指向的地址的内容:100
s3 3 指针自身的地址:0x7ffdaabbeb68
----------------------------------
s1 1 执行了无参构造函数!
s1 1 指针指向的地址:0
s1 1 指针指向的地址的内容:空指针没有指向,不能解引用操作
s1 1 指针自身的地址:0x7ffdaabbeba8
----------------------------------
s5 5 执行了拷贝赋值运算符函数!
s5 5 指针指向的地址:0x10e80f0
s5 5 指针指向的地址的内容:100
s5 5 指针自身的地址:0x7ffdaabbeba8
----------------------------------
s4 4 执行了移动构造函数!
s4 4 指针指向的地址:0x10e80f0
s4 4 指针指向的地址的内容:100
s4 4 指针自身的地址:0x7ffdaabbebe8
----------------------------------
**************************************** 0
**************************************** s5 
**************************************** 5
s1 1 执行了无参构造函数!
s1 1 指针指向的地址:0
s1 1 指针指向的地址的内容:空指针没有指向,不能解引用操作
s1 1 指针自身的地址:0x7ffdaabbec28
----------------------------------
s6 6 执行了移动赋值运算符函数!
s6 6 指针指向的地址:0x10e80f0
s6 6 指针指向的地址的内容:100
s6 6 指针自身的地址:0x7ffdaabbec28
----------------------------------
**************************************** 0
**************************************** s4 
**************************************** 4
s1 1 执行了无参构造函数!
s1 1 指针指向的地址:0
s1 1 指针指向的地址的内容:空指针没有指向,不能解引用操作
s1 1 指针自身的地址:0x7ffdaabbec68
----------------------------------
s9 9 执行了拷贝赋值运算符函数!
s9 9 指针指向的地址:0x10e8110
s9 9 指针指向的地址的内容:100
s9 9 指针自身的地址:0x7ffdaabbec68
----------------------------------
s9 9 执行了拷贝赋值运算符函数!
s9 9 指针指向的地址:0x10e8130
s9 9 指针指向的地址的内容:100
s9 9 指针自身的地址:0x7ffdaabbec68
----------------------------------
s9 9 执行了拷贝赋值运算符函数!
s9 9 指针指向的地址:0x10e8150
s9 9 指针指向的地址的内容:100
s9 9 指针自身的地址:0x7ffdaabbec68
----------------------------------
s9 9 执行了拷贝赋值运算符函数!
s9 9 指针指向的地址:0x10e8170
s9 9 指针指向的地址的内容:100
s9 9 指针自身的地址:0x7ffdaabbec68
----------------------------------
s9 9 执行了析构函数!
s6 6 执行了析构函数!
s4 4 执行了析构函数!
s5 5 执行了析构函数!
s3 3 执行了析构函数!
s2 2 执行了析构函数!
内部对象 123 执行了析构函数!
s1 1 执行了析构函数!
外部对象 123 执行了析构函数!

Process finished with exit code 0
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!