杂货边角(16):C++11之后,类的所有构造函数

news/2024/5/19 2:13:47 标签: C++11, 构造函数

C++类声明中,编译器会默认帮助程序员生成一些他们未定义的成员函数,这样的函数被称为“默认函数”,这包括了以下一些自定义类型的成员函数:
1.构造函数
2.拷贝构造函数
3.拷贝赋值函数(operator= (const Class & T) )
4.移动构造函数
5.移动拷贝赋值函数(operator= (Class&& T) )
6.析构函数
但是程序员一旦在类的定义中显式地声明上述构造函数的任何一种,则编译器将不会在为该类定义该种类下的默认版本。最常见的便是一旦声明了带参构造函数,则同时也需要程序员自己提供无参构造函数,否则该类将不存在无参构造函数。(但不是说定义了构造函数,系统不提供拷贝构造函数、移动构造函数等,即上述1、2、 3等之间彼此不冲突)。

#include <string>
#include <iostream>
#include <memory>
#include <vector>
#include <string.h>

using namespace std;

class CMyString {
private:
    char * buf;
    int len;

private:
    void copy(const char* s) {
        buf = new char[len+1];
        memcpy(buf, s, len);
        buf[len] = '\0';
    }

public:
    CMyString() {
        std::cout << "non-param constructor" << std::endl;

        buf = nullptr;
        len = 0;
    }

    CMyString(const char* str = nullptr) {
        if (str == nullptr) {
            std::cout << "one-param constructor" << std::endl;

            buf = nullptr;
            len = 0;
        }
        else {
            std::cout << "one-param constructor: " << str << std::endl;
            len = strlen(str);
            copy(str);
        }
    }

    CMyString(const CMyString& str) {                               //拷贝构造函数
        std::cout << "copy constructor: " << str.buf << std::endl;

        len = str.len;
        copy(str.buf);
    }

    CMyString(CMyString&& str) {
        std::cout << "move constructor: " << str.buf << std::endl;  //移动构造函数

        //也可以直接使用std::move

        len = str.len;
        buf = str.buf;



        str.len = 0;
        str.buf = nullptr;
    }

    CMyString& operator=(const CMyString& str) {
        std::cout << "copy and assignment func: " << str.buf << std::endl;   //拷贝赋值函数

        if (&str != this) {
            if (buf != nullptr) {
                delete[] buf;
                buf = nullptr;
            }

            len = str.len;
            copy(str.buf);
        }

        return *this;
    }



    CMyString& operator=(CMyString&& str) {
        std::cout << "move and assignment func: " << str.buf << std::endl; //移动赋值函数

        if (this != &str) {

            if (buf != nullptr) {
                delete[] buf;
                buf = nullptr;
            }

            len = str.len;
            buf = str.buf;
            str.len = 0;
            str.buf = nullptr;
        }

        return *this;
    }

    ~CMyString() {
        if (buf == nullptr)
        {
            std::cout << "destructor" << std::endl;
        }
        else
        {
            std::cout << "destructor: " << buf << std::endl;
            delete[] buf;
            buf = nullptr;
        }
    }

    void print() {
        if (buf != nullptr)
            std::cout << buf << std::endl;
        else
            std::cout << "buf is null" << std::endl;
    }


};

void func1(CMyString str) {

}

CMyString func2() {
    CMyString s("34");
    std::cout << "Resource from " << __func__ << ": " << hex << &s << endl;
    return s;
}

void func3(CMyString && str) {
    CMyString tmp = std::move(str);  //move constructor
    //CMyString tmp = str;   //copy constructor这是因为虽然str初始声明为右值引用,但一旦
    //占据表达式右值的位置时,则该右值引用的语义将变成不折不扣的左值,除非用move强制声明
}

void test0() {
    CMyString s1("12");

    func1(s1); //对象str尚未初始化,会调用拷贝构造函数

    CMyString s2(func2()); // 对象s2尚未初始化,会产生临时对象,调用移动构造函数
    std::cout << "Resource from " << __func__ << ": " << std::hex << &s2 << endl;
    //这一步存在一步编译器优化,比移动语义更加激进的优化方式,直接将func2()返回的temp对象
    //的指针,直接赋给s2,故这一步实际并没有调用任何构造函数RVO return value optimization
    //CMyString s2(std::move(func2())); //这样写则会体现出移动语义的鸠占鹊巢,存在临时对象的构造和析构

    CMyString s3 = "56";//对象s3尚未初始化,虽然是有'=6',但是其实依旧调用带参构造函数

    s3 = s1; //对象s3已初始化,会调用拷贝赋值函数

    func3(static_cast<CMyString&&> (s1) );
    s1.print();

    cout << "now comes to the destructor part:" << endl;
}

void test1() {
    CMyString s4 = "78";
    std::vector<CMyString> v1;
    //v1.push_back(s4);
    v1.push_back(std::move(s4)); // 对象尚未初始化,会调用移动构造函数

    std::cout << "v1 vector-container list:\n";
    for (auto& str : v1)
        str.print();

    std::vector<CMyString> v2;
    v2 = std::move(v1);

    std::cout << "v1 content:\n";
    for (auto& str : v1)
        str.print();

    std::cout << "v2 content:\n";
    for (auto& str : v2)
        str.print();
}

void test2() {

    CMyString s5 = "9";
    s5 = func2(); // 对象s5已初始化, 会产生临时对象,调用移动赋值函数
}

int main(void)
{
    std::cout << "begin test0()" << std::endl;
    test0();
    std::cout << std::endl;

    std::cout << "begin test1()" << std::endl;
    test1();
    std::cout << std::endl;

    std::cout << "begin test2()" << std::endl;
    test2();

    return 0;
}

这里写图片描述
这里写图片描述
这里写图片描述


http://www.niftyadmin.cn/n/1030758.html

相关文章

杂货边角(17):C++11的右值引用和移动语义

相信绝大多数人&#xff0c;在写C代码时都会被函数返回值的多次传递弄混淆过。事实上&#xff0c;这涉及到堆栈的运行&#xff0c;对于一个子函数而言&#xff0c;除非程序员显式地调用new操作符&#xff0c;否则函数创建的对象只能在自己的临时栈上&#xff0c;子函数和父函数…

杂货边角(18):POD类型

POD英文全称为Plain Old Data。是C为了保持和C的兼容性而提出的一个重要概念&#xff0c;该种数据的最主要特征是可以使用memcpy(), memset()等传统的函数直接对数据内存进行字节级别操作。 C11从两个方面来判断一个数据类型是否是POD的。考虑到C语言自带的数据类型往往都是直…

下载AssetBundle的Mono内存问题

1&#xff09;下载AssetBundle的Mono内存问题 ​2&#xff09;Unity 2019运行时获取Hierarchy上预制体资源路径 3&#xff09;多个Submeshes模型合并后的显示问题 4&#xff09;ToLua中访问Time.deltaTime为0 5&#xff09;CacheServer莫名的断开连接 这是第242篇UWA技术知识分…

杂货边角(20):匿名非受限联合体实现类的“变长成员”variant member

#include <iostream> #include <cstring>using namespace std;/* 联合体的使用&#xff0c;最主要的效果便是内存空间的节省&#xff0c;当然额外的附加作用便是灵活性&#xff0c;但是这种灵活性是以牺牲 可读性为代价的 */ union T {string s;//因为string 是非P…

杂货边角(21):用户自定义字面量 operator _X

在C编程中&#xff0c;用户自定义类或结构体是难以避免的事情&#xff0c;所以一旦遇到接受用户自定义的类或结构体作为参数的函数&#xff0c;那么必然难逃”先初始化变量–变量传值或传引用调用函数“&#xff0c;这就导致了编程过程中出现的不直观。 struct T {...}; void …

活动 | UWA DAY 2021 开启报名!

UWA DAY 2021 又要和大家见面啦&#xff01;由侑虎科技主办&#xff0c;以游戏开发为主题的第五届UWA DAY技术大会将在上海隆重召开。本次大会的主题是“助力游戏研发迈入工业化时代”&#xff0c;UWA将带领行业开发者品鉴业界的卓越研发理念和实战心得&#xff0c;对“工业化”…

杂货边角(22):名字空间namespace的一二事

namespace一直是C编程中封装性的重要概念&#xff0c;但是关于namespace的使用此前一直并没有太多总结&#xff0c;这里结合namespace的使用和C11关于名字空间的新特性总结如下。 目录&#xff1a; Sec1 子命名空间间的信息交互Sec2 不同名字空间下的模板类特化问题Sec3 终极一…

杂货边角(23):模板编程的SFINAE机制和enable_if使用

SFINAE SFINAE全称&#xff1a;Substitution Failure is not an error.即在对模板函数调用进行实例化推导匹配时出现的无效函数将被删除&#xff0c;并且编译器不会报错&#xff0c;只有最终有一个可以匹配该次调用的实例化match即可。 在对一个函数调用进行模板推导时&#…