• C++ 对象初始化和赋值


    1.区分初始化和赋值
      在C++中,当一个新对象被创建时,会有初始化操作;而赋值是修改一个已经存在的对象的值。
      初始化操作由构造函数完成,而赋值操作由operator=操作符函数完成。如果程序员没有提供构造函数和operator=操作符函数,那么编译器会提供缺省版本,使用缺省构造函数或缺省复制构造函数初始化对象,使用缺省operator=操作符函数执行赋值操作。举例如下:

    View Code
    /**
     * @file Main.cpp
     */
    #include <iostream>
    using std::cout;
    using std::endl;
    
    class CDummy
    {
    public:
        CDummy(void);
        CDummy(const CDummy& dummy);
        CDummy& operator=(const CDummy& dummy);
    };
    
    CDummy::CDummy()
    {
        cout << "无参构造" << endl;
    }
    
    CDummy::CDummy(const CDummy& /*dummy*/)
    {
        cout << "复制构造" << endl;
    }
    
    CDummy& CDummy::operator=(const CDummy& /*dummy*/)
    {
        cout << "赋值操作" << endl;
        return *this;
    }
    
    int main(int argc, char* argv[])
    {
        CDummy d1;      //初始化,无参构造函数被调用
        CDummy d2 = d1; //初始化,复制构造函数被调用
        CDummy d3;      //初始化,无参构造函数被调用
        d3 = d1;        //赋值,operator=操作符函数被调用
        return 0;
    }

      另外要注意,在赋值时被赋值对象已经有值,因此可能需要对原值做适当处理,特别是资源的归还和重新申请等等。在这方面,String类的operator=操作符函数的实现是典型例子。

    2.关于成员初始化列表和构造函数内赋值操作
      先看示例程序:

    View Code
    class PhoneNumber
    {
    };
    
    class AddressBookEntry
    {
    public:
        AddressBookEntry(const string& name, 
                         const string& address, 
                         const list<PhoneNumber>& phones);
    private:
        string _name;
        string _address;
        list<PhoneNumber> _phones;
    };
    
    AddressBookEntry::AddressBookEntry(const string& name, 
                                       const string& address, 
                                       const list<PhoneNumber>& phones)
    {
        _name = name;       //这些都是赋值,而非初始化
        _address = address;
        _phones = phones;
    }

      C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。在AddressBookEntry构造函数内,_name,_address,_phones都不是被初始化,而是被赋值。初始化的发生时间更早,发生于这些成员的default构造函数被自动调用之时(比进入AddressBookEntry构造函数本体的时间更早)。
      AddressBookEntry构造函数的一个较佳写法是,使用成员初始化列表代替赋值动作:

    View Code
    AddressBookEntry::AddressBookEntry(const string& name,
                                       const string& address,
                                       const list<PhoneNumber>& phones)
                                       :_name(name),       //现在,这些都是初始化
                                        _address(address),
                                        _phones(phones)
    {
    }

      赋值的那个版本先调用default构造函数然后再调用operator=操作符函数;而成员初始化列表的那个版本只调用一次copy构造函数,是比较高效的,有时甚至高效得多。
      另外,对于内置类型对象,其初始化和赋值的成本相同。但是有些情况下即使成员变量属于内置类型,也一定得使用成员初始化列表。是的,如果成员变量是 const 或 references,它们就一定需要初值,不能被赋值。

    3.关于成员初始化次序
      C++有着十分固定的成员初始化次序。是的,次序总是相同:base classes 更早于其 derived classes被初始化,而 class 的成员变量总是以其声明次序被初始化。
      另外,为避免“跨编译单元之初始化次序”问题,请以 local static 对象替换 non-local static 对象。这是 Singleton 模式的一个常见实现手法。(ACE里面的单例大多是这么实现的,原来是有原因的:))


    参考文献:
    Effective C++ 3rd Edition

  • 相关阅读:
    Java 8 ThreadLocal 源码解析
    RabbitMQ 消息中间件
    MySQL 索引与查询优化
    MySQL EXPLAIN 命令: 查看查询执行计划
    迎来送往,开启新篇章
    mockito的用法
    推荐一个计算机的科普视频
    Golang查缺补漏(一)
    2019定个小目标
    golang 中的指针
  • 原文地址:https://www.cnblogs.com/codingthings/p/2470610.html
Copyright © 2020-2023  润新知