• C++编译器自动生成的函数


    https://www.programminghunter.com/article/4569180387/
    在C++中当创建一个空类时,C++就会默认的为这个类创建4个函数:默认的构造函数、析构函数、拷贝构造函数、以及赋值操作符。本文参考Effective C++介绍这几个函数。

    1.函数的原型以及函数创建的时机

    C++中创建一个空类:

    class Empty {};
    

    默认会生成4个函数,其函数的原型如下:

    public:
        Empty() { ... }
        Empty(const Empty& rhs) { ... }
        ~Empty() { ... }
        Empty& operator=(const Empty& rhs) { ... }
    

    说明:1) 这些函数只有在需要调用的时候,编译器才会生成。2) 4个函数都是public的。3) 4个函数都是inline的(即函数定义在类的定义中的函数)。4) 如果你显式的声明了这些函数中的任何一个函数,那么编译器将不再生成默认的函数。

    比如,当遇到下列语句时,函数会被编译器生成:

    Empty e1;                //默认构造函数
                             //对象销毁时,析构函数
    Empty e2(e1);            //拷贝构造函数
    e2 = e1;                 //赋值运算符
    

    另外,还存在两种默认的函数:就是取地址运算符和取地址运算符的const版本,这两个函数在《Effective C++》中没有提及。

    public:
        Empty* operator&() { ... }
        const Empty* operator&() const { ... }
    

    这两个函数是确实存在的,正如下面的代码可以正常工作:

    #include <stdio.h>
    
    class Empty {
    };
    
    int main(int argc, char** argv)
    {
            Empty a;
            const Empty *b = &a;
            printf("%p\n", &a);             //调用取地址运算符
            printf("%p\n", b);              //调用const取地址运算符
    }
    

    一个容易被忽略的问题:自定义的拷贝构造函数不仅会覆盖默认的拷贝构造函数,也会覆盖默认的构造函数。下面的代码是编译不过的,用户必须再显式的定义一个无参的构造函数。

    class Empty {
    public:
        Empty(const Empty& e) { }    //拷贝构造函数
    };
    
    int main(int argc, char** argv)
    {
        Empty a;
    }
    

    2. 赋值操作符存在的问题

    赋值操作符函数的行为与拷贝构造函数的行为基本是相同的,编译器生成赋值操作符函数是有条件的,如果会产生无法完成的操作,编译器将拒绝产生这一函数。那么什么时候编译器无法完成赋值这一行为呢?考虑如下情形(来源Effective C++):

    template<class T>
    class NameObject {
    public:
        NameObject(std::string& name, const T& value);
    private:
        std::string& nameValue;    //引用成员变量
        const T objectValue;       //const成员变量
    };
    

    然后考虑下面的语句会发生什么事:

    std::string newDog("abc");
    std::string oldDog("xxx");
    NameObject<int> p(newDog, 2);
    NameObject<int> s(oldDog, 10);
    p = s;                           //将会发生什么?
    

    赋值语句之前,p.nameValue指向newDog, s.nameValue指向oldDog。那么赋值之后呢?p.nameValue应该指向s.nameValue指向的对象吗?但是C++有一条规定:引用不能改指向另外一个对象。

    对于变量objectValue,C++规定:更改const成员是不合法的。

    因此如果上面两种情形中的任何一种发生了,C++编译器给出的响应是:拒绝编译这一行的赋值动作。如果你这么做了,C++编译器会报错。如果你执意要进行赋值操作,那么可以自己定义一个赋值操作符重载函数。

    3. c++0x中的新变化

    C++0x中引入了“右值引用”和“移动语义”的概念,可以实现对右值的引用。(左值和右值的解释可以见http://amyz.itpub.net/post/34151/411832)

    移动语义,简单来说,就是在一个右值对象的生命期结束之前,将其资源偷过来,为我所用。有关移动语义的详细内容这里不做详述,大家可以参见csdn上一篇文章 http://blog.csdn.net/pongba/article/details/1684519。这里要说明的是移动构造函数和移动赋值运算符。

    1. 移动构造函数和移动赋值运算符重载函数不会隐式声明,必须自己定义。

    2. 如果用户自定义了拷贝构造函数或者移动构造函数,那么默认的构造函数将不会隐式定义,如果用户需要,也需要显式的定义。

    3. 移动构造函数不会覆盖隐式的拷贝构造函数。

    4. 移动赋值运算符重载函数不会覆盖隐式的赋值运算符重载函数。

  • 相关阅读:
    java实现机器人行走
    java实现机器人行走
    java实现机器人行走
    java实现机器人行走
    java实现机器人行走
    java实现购物券消费方案
    java实现购物券消费方案
    java实现购物券消费方案
    java实现购物券消费方案
    《技术垄断:文明向技术投降》
  • 原文地址:https://www.cnblogs.com/islch/p/16143768.html
Copyright © 2020-2023  润新知