zoukankan      html  css  js  c++  java
  • [Effective C++ --005]了解C++默默编写并调用哪些函数

    <前言>
    编译器是个十分敬业的工作者,不但为你编译代码,甚至为你生成代码,不可思议吧。本文主要介绍编译器究竟会为我们生成和调用哪些代码。

    <空类和非空类>
    如果问什么样的类是空类?也许你会说下面的类就是空类。

    class A{
    
    };

    好吧,我也是这么认为的。没有构造函数怎么创建对象?事实上编译器会自动地把A的定义转为下面这样的代码。

    class A{
    public:
    A() { }
    A(const A& rhs) { ... }
    ~A() { }
    
    A& operator =(const A& rhs) { ... }
    };

    需要注意的是:只有这些函数被调用的时候,它们才会被创建出来。也就是当下面的代码存在时两者才是等同的。产生的析构函数是non-virtual。
    A a0; // 默认构造函数
    // 默认析构函数
    A a1(a0); // copy构造函数
    a0 = a1; // 赋值运算符
    如果想禁止编译器自动生成这些函数该怎么办呢?答案是你自己显式的声明它们。

     <各生成函数的介绍>

    3.1.<构造函数和析构函数>
    假如有一个关于Dog的类,包含Dog的Name和age信息。类的定义如下面的代码。

    class Dog {
    private:
        std::string name;
        int age;
    public:
        Dog(const char* name, const int age);
        Dog(const std::string& name, const int age);
        ~Dog(){ }
    };


    这里声明了有参数的构造函数,所以编译器就不会为我们生成默认的构造函数了。析构函数的定义规则也是一样的。

    下面介绍Copy构造函数和赋值运算符。

    3.2.<Copy构造函数和赋值运算符>
    邻居家有两条狗:大黄1和大黄2,而且它们是双胞胎。
    Dog bigYellow1("Big Yellow",3);
    Dog bigYellow2(bigYellow); // 调用Copy构造函数
    由编译器生成的Copy构造函数以bigYellow1.name和bigYellow1.age为初值来设定bigYellow2.name和bigYellow2.age。
    两者之中,name的类型是string,幸运的是string类型有Copy构造函数,所以name属性的Copy是成功的。age的类型是int型,属于
    基本数据类型,所以age的Copy是按字节Copy(Bitwise Copy)的,不需要Copy构造函数。
    赋值运算符和Copy构造函数其行为基本上一致,但也是有区别的(传送门:http://www.cnblogs.com/whyandinside/archive/2012/05/12/2497237.html)。

    那是不是编译器这种生成操作总能成功呢?答案当然是否定的。请看下面例子。

    class Dog {
    private:
        std::string& name;
        const int age;
    public:
        Dog(const std::string& name, const int age);
        ~Dog(){ }
    };
    std::string name1("Big Yellow");
    std::string name2("Big Yellow");
    Dog bigYellow1(name1,3); // 大黄1于去年归西了
    Dog bigYellow2(name2,4);
    bigYellow1 = bigYellow2; // 大黄2取代了大黄1的位置

    请注意与前一个例子的区别:Dog(const char* name, const int age);构造函数被删除了,age的类型被限定为const,name被声明为string的引用。
    虽然name1和name2的内容都是“Big Yellow”,但存储的位置不同,也就是地址不同。那么进行赋值的时候会发生什么呢?
    编译器试图把bigYellow2.name拷贝到bigYellow1.name字段中。因为name是引用类型,C++中并不允许让引用改指向不同对象,所以编译器对这样的赋值操作无可奈何。
    对此,编译器就不会生成默认的赋值运算符,因此需要我们自己去实现。
    面对age的赋值,编译器的反应也是一样的。因为更改const成员是不合法的。还有一种情况编译器也不会生成默认的赋值操作符。
    当父类将赋值运算符声明为private,编译器将拒绝为其子类生成赋值运算符。因为无法调用子类都无权调用的成员函数。


    ■总结■
    1)编译器可以自动创建默认构造函数、析构函数、Copy构造函数和赋值运算符。
    2)如果不希望编译器这么做,需要显式定义它们。
    3)默认赋值运算符不能生成的3种情况:成员是引用型、const型或者父类的赋值运算符声明为private。
    4)GUI项目中基本上类的构造函数、析构函数都是显示声明的,Copy构造函数和赋值运算符目前没有发现有显式声明的。

  • 相关阅读:
    How to run a batch file each time the computer loads Windows
    go.mod file not found in current directory or any parent directory; see 'go help modules'
    xshell 所选的用户密钥未在远程主机上注册;无法加载密钥
    群起Hadoop的一个错误
    ssh: connect to host hadoop102 port 22: No route to host
    VMware下centos7配置静态ip并解决ping不通百度的问题
    虚拟机CentOS 7 网络连接显示"以太网(ens33,被拔出)"
    Rust-线程:使用消息传递在线程间传送数据
    Rust-线程,使用线程同时运行代码
    Rust-智能指针:RefCell<T>和内部可变性模式
  • 原文地址:https://www.cnblogs.com/hustcser/p/4087605.html
Copyright © 2011-2022 走看看