zoukankan      html  css  js  c++  java
  • <Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<二>

    <Item 9> Never call virtual functions during construction or destruction

    1、you shouldn't call virtual functions during construction or destruction, because the calls won't do what you think, and if they did, you'd still be unhappy. If you're a recovering Java or C# programmer, pay close attention to this Item, because this is a place where those languages zig, while C++ zags.java可以在构造函数中调用子类函数,但是子类成员变量此时没有初始化。

    2、During base class construction, virtual functions never go down into derived classes. Instead, the object behaves as if it were of the base type. Informally speaking, during base class construction, virtual functions aren't.

    3、It's actually more fundamental than that. During base class construction of a derived class object, the type of the object is that of the base class. Not only do virtual functions resolve to the base class, but the parts of the language using runtime type information (e.g., dynamic_cast (see Item 27) and typeid) treat the object as a base class type. An object doesn't become a derived class object until execution of a derived class constructor begins.

    Upon entry to the base class destructor, the object becomes a base class object, and all parts of C++ — virtual functions, dynamic_casts, etc., — treat it that way.

     4、不小心在构造函数或者析构函数中间接调用虚函数是C++的常见错误,因此最好在编码规范中严格进行限制。

    class Transaction {
    
    public:
      Transaction()
      { init(); }                                      // call to non-virtual...
    
      virtual void logTransaction() const = 0;
      ...
    
    private:
    
      void init()
      {
        ...
        logTransaction();                              // ...that calls a virtual!
      }
    };

     可以在派生类中定义必要的static函数向基类构造函数传递必要的信息

    class Transaction {
    
    public:
      explicit Transaction(const std::string& logInfo);
      void logTransaction(const std::string& logInfo) const;   // now a non-
                                                               // virtual func
      ...
    };
    
    Transaction::Transaction(const std::string& logInfo)
    {
      ...
      logTransaction(logInfo);                                // now a non-
    }                                                         // virtual call
    
    class BuyTransaction: public Transaction {
    public: BuyTransaction( parameters ) : Transaction(createLogString( parameters )) // pass log info { ... } // to base class ... // constructor
    private: static std::string createLogString( parameters ); };

     Using a helper function to create a value to pass to a base class constructor is often more convenient (and more readable) that going through contortions in the member initialization list to give the base class what it needs.

    5、Things to Remember

    • Don't call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor.

    <Item 10>Have assignment operators return a reference to *this

     6、让=、+=、-=、*=等赋值运算符的重载函数return *this,这样可以实现类似built-in变量的功能   code that doesn't follow it will compile. However, the convention is followed by all the built-in types as well as by all the types in (or soon to be in — see Item54) the standard library (e.g., string, vector, complex, tr1::shared_ptr, etc.). Unless you have a good reason for doing things differently, don't.

    int x, y, z;
    x = y = z = 15;                        // chain of assignments

    7、Things to Remember

    • Have assignment operators return a reference to *this.

    <Item 11> Handle assignment to self in operator=

    8、通过数组循环、指针引用、基类和派生类的指针混用,很容易导致对象自己向自己赋值。如下使用对象管理资源的代码,自我赋值的时候就会导致资源被错误的delete

    class Bitmap { ... };
    
    class Widget {
      ...
    
    private:
      Bitmap *pb;                                     // ptr to a heap-allocated object
    };

    Widget& Widget::operator=(const Widget& rhs)              // unsafe impl. of operator=
    
    {
      delete pb;                                      // stop using current bitmap
      pb = new Bitmap(*rhs.pb);                       // start using a copy of rhs's bitmap
    
      return *this;                                   // see Item 10
    }

    9、传统的保护方式如下,这种方法解决了保护了自我赋值的问题,但是还会有另外一个异常安全问题,假如new Bitmap(*rhs.pb)抛出异常,会导致pb被指向一个被delete的bitmap对象

    Widget& Widget::operator=(const Widget& rhs)
    {
      if (this == &rhs) return *this;   // identity test(证同测试): if a self-assignment,
                                        // do nothing
    
      delete pb;
      pb = new Bitmap(*rhs.pb);
      return *this;
    
    }

     a careful ordering of statements can yield exception-safe (and self-assignment-safe) code

    Widget& Widget::operator=(const Widget& rhs)
    {
    
      Bitmap *pOrig = pb;               // remember original pb
      pb = new Bitmap(*rhs.pb);         // make pb point to a copy of *pb
      delete pOrig;                     // delete the original pb
    
      return *this;
    }

    If you're concerned about efficiency, you could put the identity test back at the top of the function. Before doing that, however, ask yourself how often you expect self-assignments to occur, because the test isn't free. It makes the code (both source and object) a bit bigger, and it introduces a branch into the flow of control, both of which can decrease runtime speed. The effectiveness of instruction prefetching, caching, and pipelining can be reduced, for example.

    10、An alternative to manually ordering statements in operator= to make sure the implementation is both exception- and self-assignment-safe is to use the technique known as "copy and swap." This technique is closely associated with exception safety, so it's described in Item 29. However, it's a common enough way to write operator= that it's worth seeing what such an implementation often looks like

    class Widget {
      ...
      void swap(Widget& rhs);   // exchange *this's and rhs's data;
      ...                       // see Item 29 for details
    };
    
    
    Widget& Widget::operator=(const Widget& rhs)
    {
      Widget temp(rhs);             // make a copy of rhs's data
      swap(temp);                   // swap *this's data with the copy's
    
      return *this;
    }

    A variation on this theme takes advantage of the facts that (1) a class's copy assignment operator may be declared to take its argument by value and (2) passing something by value makes a copy of it (see Item 20):

    Widget& Widget::operator=(Widget rhs)   // rhs is a copy of the object
    {                                       // passed in — note pass by val
      swap(rhs);                            // swap *this's data with
                                            // the copy's
      return *this;
    } 

    Personally, I worry that this approach sacrifices clarity at the altar of cleverness, but by moving the copying operation from the body of the function to construction of the parameter, it's a fact that compilers can sometimes generate more efficient code.

    11、Things to Remember

    • Make sure operator= is well-behaved when an object is assigned to itself. Techniques include comparing addresses of source and target objects, careful statement ordering, and copy-and-swap.

    • Make sure that any function operating on more than one object behaves correctly if two or more of the objects are the same.

    <Item 12>Copy all parts of an object

     12、In well-designed object-oriented systems that encapsulate the internal parts of objects, only two functions copy objects: the aptly named copy constructor and copy assignment operator. We'll call these the copying functions. Item 5 observes that compilers will generate the copying functions, if needed, and it explains that the compiler-generated versions do precisely what you'd expect: they copy all the data of the object being copied.

    13、 if you add a data member to a class, you need to make sure that you update the copying functions, too. (You'll also need to update all the constructors (see Items 4 and 45) as well as any non-standard forms of operator= in the class (Item 10 gives an example). If you forget, compilers are unlikely to remind you.)

    14、One of the most insidious ways this issue can arise is through inheritance.Instead, derived class copying functions must invoke their corresponding base class functions:

    void logCall(const std::string& funcName);          // make a log entry
    
    class Customer {
    public:
      ...
      Customer(const Customer& rhs);
      Customer& operator=(const Customer& rhs);
      ...
    
    private:
      std::string name;
    };

    class
    PriorityCustomer: public Customer { // a derived class public: ... PriorityCustomer(const PriorityCustomer& rhs); PriorityCustomer& operator=(const PriorityCustomer& rhs); ... private: int priority; }; PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs) : Customer(rhs), // invoke base class copy ctor priority(rhs.priority) { logCall("PriorityCustomer copy constructor"); } PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs) { logCall("PriorityCustomer copy assignment operator"); Customer::operator=(rhs); // assign base class parts priority = rhs.priority; return *this; }

     假如没有红色代码处理基类数据,复制构造函数将会调用基类的默认构造函数初始化基类的变量,如果没有默认构造函数,代码不会编译通过,复制赋值操作符将会不会对基类数据作操作,基类数据保持不变。

    15、When you're writing a copying function, be sure to(1) copy all local data members and (2) invoke the appropriate copying function in all base classes, too.

    16、copy assignment operator和copy constructor代码相似但是不能相互调用,因为一个是改变已有对象,一个是创建新对象。Instead, if you find that your copy constructor and copy assignment operator have similar code bodies, eliminate the duplication by creating a third member function that both call. Such a function is typically private and is often named init. This strategy is a safe, proven way to eliminate code duplication in copy constructors and copy assignment operators.

    17、Things to Remember

    • Copying functions should be sure to copy all of an object's data members and all of its base class parts.

    • Don't try to implement one of the copying functions in terms of the other. Instead, put common functionality in a third function that both call.

  • 相关阅读:
    python作业堡垒机(第十三周)
    Python的一个BOX
    Nginx (限速)限制并发、限制访问速率、限制流量
    Nginx反向代理维基百科镜像制作全解析
    防盗链测试01
    Pycharm和IntelliJ IDEA激活 2017.3.x版本
    [Openwrt 扩展下篇] Openwrt搭建私有云Owncloud 9
    [Openwrt扩展中篇]添加Aria2和webui
    [Openwrt 扩展上篇]USB挂载&U盘启动&Samba共享
    python作业(第十一周)基于RabbitMQ rpc实现的主机管理
  • 原文地址:https://www.cnblogs.com/lshs/p/4421647.html
Copyright © 2011-2022 走看看