C++类基础(五)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

构造、析构与复制成员函数
● 构造函数构造对象时调用的函数

class Str
{
public:
    //构造函数名与类名相同不需要返回值目的是构造一个该类型的对象默认的返回类型是该类类型
    Str() //缺省构造函数
    {
        std::cout << "Str() is called\n";
    }
    Str(int input) //参数列表不同构成重载该构造函数用于初始化私有数据x
    {
        x = input;
        std::cout << "Str(int input) is called\t" << x << std::endl;
    }
private:
    int x;
};

int main()
{
    Str m;
    std::cout << "end this line\n";
    Str m2(3);
    return 0;
}

在这里插入图片描述
– 名称与类名相同无返回值可以包含多个版本重载
– C++11 代理构造函数

class Str
{
public:
    /*Str() //#1
    {
        x = 3;
        std::cout << "Str() is called\n";
    }*/
    Str() : Str(3) //原始构造函数
    {
        std::cout << "Str() : Str(3)\n";
    }
    //Str (int input = 3)  //与#1构造函数内部逻辑相同无代理构造函数时直接注释掉#1构造函数
    Str(int input) //代理构造函数since C++11
    {
        x = input;
        std::cout << "Str(int input) is called\t" << x << std::endl;
    }
private:
    int x;
};

int main()
{
    Str m(30);
    std::cout << "End this line\n";
    Str m2; //C++标准: 执行代理构造函数时,先执行代理函数的逻辑在执行原始构造函数的逻辑
    return 0;
}

在这里插入图片描述

● 初始化列表区分数据成员的初始化与赋值
– 通常情况下可以提升系统性能

class Str
{
public:
    Str(const std::string& val)
    {
        std::cout << "Pre-assignment: " << x << std::endl; //字符串的缺省初始化会将字符串初始化成空字符串
        x = val; //核心的写操作, 是类类型的赋值
        std::cout << "Post-assignment: " << x << std::endl;
    }
private:
    std::string x;
};

int main()
{
    Str m("abc"); //#1, 该行代码相当于#2、#3行代码的效果性能不佳

    std::string x; //#2
    x = "abc"; //#3

    int var = 3; //初始化: 为变量申请内存空间并将该内存空间的值修改为等号后面的值
    var = 5; //赋值: 只是将该内存空间的值修改为等号后面的值
    return 0;
}

在这里插入图片描述

class Str
{
public:
    Str(const std::string& val) : x(val), y(0) //x(val), y(0)是初始化列表使用val初始化私有数据x使用0初始化y
    {
        std::cout << "Pre-assignment: " << x << '\t' << y << std::endl; //字符串的缺省初始化会将字符串初始化成空字符串
        std::cout << "Post-assignment: " << x << '\t' << y << std::endl;
    }
private:
    std::string x;
    int y;
};

int main()
{
    Str m("abc"); //使用列表初始化, 该行代码相当于#2行代码的效果性能提升

    std::string x = "abc"; //#2
    return 0;
}

在这里插入图片描述

– 一些情况下必须使用初始化列表如类中包含引用成员

class Str
{
public:
    Str(const std::string& val, int& _ref) //注意第二参数类型必须是int&: 如果是int _refref会绑定到形参_ref临时对象构造函数结束后形参销毁形成dangling reference
        : x(val)
        , y(0)
        , ref(_ref)
    {
        ref = 3;
    }
private:
    std::string x;
    int y;
    int &ref;
};

int main()
{
    int val = 0;; //#1
    std::cout << val <<std::endl;
    Str m("abc", val); //OK类Str的私有数据引用ref绑定到#1的val
    std::cout << val <<std::endl;
    return 0;
}

在这里插入图片描述

– 注意元素的初始化顺序与其声明顺序相关与初始化列表中的顺序无关

class Str
{
public:
    Str(const std::string& val) : x(val) , y(x.size()) //OK先后初始化x, yC++标准
    //Str(const std::string& val) : y(x.size()), x(val)  //Warning even Error: Field 'y' will be initialized after field 'x'
    {
        std::cout << x << '\t' << y <<std::endl;
    }
private:
    std::string x; //先声明x
    size_t y; //后声明y
};

int main()
{
    Str m("abc");
    return 0;
}

笔记在大量的应用背景下C++抽象成用栈去存储自动存储持续变量因此对象或者数据成员的构造顺序与销毁顺序恰好相反。如果对象数据成员的销毁顺序与初始化列表顺序相反当类中有大量的数据成员用不同的初始化顺序来初始化时编译器要记录每一个类对象的数据成员初始化顺序加重了编译器负担与C++强调性能背道而驰。所以无论初始化列表顺序是什么初始化顺序只与声明顺序一致

class Str
{
public:
    Str(size_t input) : x(y+1) , y(input) //声明私有数据是顺序: 先声明x后声明y。用y值初始化x值时会出现意料之外的值
    {
        std::cout << x << '\t' << y <<std::endl;
    }
private:
    size_t x; //先声明x
    size_t y; //后声明y
};

int main()
{
    Str m(4);
    return 0;
}

在这里插入图片描述

– 使用初始化列表覆盖类内成员初始化的行为

class Str
{
public:
    Str()
    {
        std::cout << x << '\t' << y <<std::endl;
    }
private:
    size_t x = 3; //类内成员初始化
    size_t y = 4; //类内成员初始化
};

int main()
{
    Str m;
    return 0;
}

在这里插入图片描述

class Str
{
public:
    Str() : x(10), y(11) //初始化列表覆盖类内成员初始化输出10 11
    {
        std::cout << x << '\t' << y <<std::endl;
    }
private:
    size_t x = 3; //类内成员初始化
    size_t y = 4; //类内成员初始化
};

int main()
{
    Str m;
    return 0;
}

在这里插入图片描述

● 缺省构造函数不需要提供实际参数就可以调用的构造函数

class Str
{
public:
    Str() //缺省构造函数无参数就可以调用
    {
        std::cout << x << '\t' << y <<std::endl;
    }
private:
    size_t x = 3;
    size_t y = 4;
};

int main()
{
    Str m;
    return 0;
}

– 如果类中没有提供任何构造函数那么在条件允许的情况下编译器会合成一个缺省构造函数

struct Str
{
    size_t x;
    size_t y;
    std::string val;
    int& ref; //编译器无法拒绝对引用缺省初始化
};

– 合成的缺省构造函数会使用缺省初始化来初始化其数据成员

struct Str
{
    size_t x;
    size_t y;
    std::string val;
};

int main()
{
    Str m; // OK,编译器会自动合成缺省构造函数与C兼容
    std::cout << m.x << ' ' << m.y << ' ' << m.val <<std::endl;
    return 0;
}

在这里插入图片描述
– 调用缺省构造函数时避免 most vexing parse

struct Str
{
    size_t x;
    size_t y;
    std::string val;
};

int main()
{
    Str m(); //Error: most vexing parse此处是声明了一个无参的函数
    Str m2{}; //OK
    return 0;
}

– 使用 default 关键字定义缺省构造函数

struct Str
{
    Str() = default; //OK, since C++11与编译器合成的缺省构造函数的逻辑构造对象
    Str(const std::string& input)
        : val(input) {}
    std::string val;
};

int main()
{
    Str m{};
    std::cout << m.val << std::endl;
    return 0;
}

在这里插入图片描述
参考
深蓝学院C++基础与深度解析

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: c++