C++ Chapter 4. Designs and Declarations

1.

Once the right types are in place, it can sometimes be reasonable to restrict the values of those types. For example, there are only 12 valid month values, so theMonth type should reflect that. One way to do this would be to use an enum to represent the month, but enums are not as type-safe as we might like. For example, enums can be used likeints (seeItem 2). A safer solution is to predefine the set of all validMonths:

class Month {

public:

  static Month Jan() { return Month(1); }   // functions returning all valid

  static Month Feb() { return Month(2); }   // Month values; see below for

  ...                                       // why these are functions, not

  static Month Dec() { return Month(12); }  // objects

  

  ...                                       // other member functions



private:

  explicit Month(int m);                    // prevent creation of new

                                            // Month values



  ...                                       // month-specific data

};

Date d(Month::Mar(), Day(30), Year(1995));


C++ constructors that have just one parameter automatically perform implicit type conversion. For example, if you pass an int when the constructor expects a string pointer parameter, the compiler will add the code it must have to convert the int to a string pointer. However, you might not always want this automatic behavior.

Implicit Type Conversion:

1. operator T1()

2. constructor with only one parameter and without the keyword explict(Constructor with multi parameters but only one of them doesn't have default value also works as implicit constructor)

You can add explicit to the constructor declaration to prevent implicit conversions. This forces the code to either use a parameter of the correct type, or cast the parameter to the correct type. That is, if the cast is not visibly expressed in code, an error will result.

The explicit keyword can only be applied to in-class constructor declarations to explicitly construct an object.


2.slicing problem. When a derived class object is passed (by value) as a base class object, the base class copy constructor is called, and the specialized features that make the object behave like a derived class object are "sliced" off. You're left with a simple base class object — little surprise, since a base class constructor created it.  We can use reference to avoid that problem because:

If you peek under the hood of a C++ compiler, you'll find that references are typically implemented as pointers, so passing something by reference usually means really passing a pointer.


3.

  • Prefer pass-by-reference-to-const over pass-by-value. It's typically more efficient and it avoids the slicing problem.

  • The rule doesn't apply to built-in types and STL iterator and function object types. For them, pass-by-value is usually appropriate

    Never return a pointer or reference to a local stack object, a reference to a heap-allocated object, or a pointer or reference to a local static object if there is a chance that more than one such object will be needed(because there is only one copy of the static object). 


4.

  • Prefer non-member non-friend functions to member functions. Doing so increases encapsulation, packaging flexibility, and functional extensibility
    Encapsulation:

    non-member, non-friend function will have no access to class's private data.

    Packaging Flexibility:

    It can be divided to different namespace in different header files rather than in a single class.Such as <vector> <list>

    functional extensibility:

    Client can create their own function set.

5. 

  • If you need type conversions on all parameters to a function (including the one pointed to by thethis pointer), the function must be a non-member.

class Rational {



  ...                                             // contains no operator*

};

const Rational operator*(const Rational& lhs,     // now a non-member

                         const Rational& rhs)     // function 

{

  return Rational(lhs.numerator() * rhs.numerator(),

                  lhs.denominator() * rhs.denominator());

}

If operator* is in member function, it can only have one parameter.


6. 

class Widget {                     // same as above, except for the

public:                            // addition of the swap mem func

  ...

  void swap(Widget& other)

  {

    using std::swap;               // the need for this declaration

                                   // is explained later in this Item



    swap(pImpl, other.pImpl);      // to swap Widgets, swap their

  }                                // pImpl pointers

  ...

};



namespace std {



  template<>                       // revised specialization of

  void swap<Widget>(Widget& a,     // std::swap

                    Widget& b)

  {

    a.swap(b);                     // to swap Widgets, call their

  }                                // swap member function

}

The "template<>" at the beginning of this function says that this is a total template specialization for std::swap, and the "<Widget>" after the name of the function says that the specialization is for when T is Widget. In other words, when the generalswap template is applied to Widgets, this is the implementation that should be used. 


In general, we're not permitted to alter the contents of the std namespace, but we are allowed to totally specialize standard templates (likeswap) for types of our own creation (such as Widget). The code below is not legal because we add a new type into std.

namespace std {



  template<typename T>             // an overloading of std::swap

  void swap(Widget<T>& a,          // (note the lack of "<...>" after

            Widget<T>& b)          // "swap"), but see below for

  { a.swap(b); }                   // why this isn't valid code

}


7. 

if the default implementation of swap isn't efficient enough (which almost always means that your class or template is using some variation of the pimpl idiom), do the following:

  1. Offer a public swap member function that efficiently swaps the value of two objects of your type. this function should never throw an exception.

    class Widget {                     // same as above, except for the
    
    public:                            // addition of the swap mem func
    
      ...
    
      void swap(Widget& other)
    
      {
    
        using std::swap;               // the need for this declaration
    
                                       // is explained later in this Item
    
    
    
        swap(pImpl, other.pImpl);      // to swap Widgets, swap their
    
      }                                // pImpl pointers
    
      ...
    
    };
    
    

  2. Offer a non-member swap in the same namespace as your class or template. Have it call your swap member function.

    namespace WidgetStuff {
    
      ...                                     // templatized WidgetImpl, etc.
    
    
    
      template<typename T>                    // as before, including the swap
    
      class Widget { ... };                   // member function
    
    
    
      ...
    
    
    
      template<typename T>                    // non-member swap function;
    
      void swap(Widget<T>& a,                 // not part of the std namespace
    
                Widget<T>& b)                                         
    
      {
    
        a.swap(b);
    
      }
    
    }
    
    

  3. If you're writing a class (not a class template), specialize std::swap for your class. Have it also call your swap member function.

    namespace std {
    
    
    
      template<>                       // revised specialization of
    
      void swap<Widget>(Widget& a,     // std::swap
    
                        Widget& b)
    
      {
    
        a.swap(b);                     // to swap Widgets, call their
    
      }                                // swap member function
    
    }
    
    

Finally, if you're calling swap, be sure to include ausing declaration to makestd::swap visible in your function, then callswap without any namespace qualification.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值