zoukankan      html  css  js  c++  java
  • 《Effective Modern C++》

    下面面四个表达式中前两个实际上是声明一个值为27int类型的变量。而后两个,是声明一个类型为std::initializer_list<int>,并且具有一个值为27的元素!

    auto x1 = 27;        //type is int, value is 27
    auto x2(27);         //同上
    auto x3 = {27};      //type is std::initializer_list<int>, value is {27}
    auto x4{27};         //同上

    这是因为auto类型推导有特俗的规则。当为auto声明的类型进行初始化使用封闭的大括号时,则推导的类型为std::initializer_list

    C++14允许使用auto去推导函数的返回值(参见条款3),并且C++14中的lambda表达式在参数声明时可以使用auto类型推导。但是,这里使用的auto推导采用的是模板推导的规则,而不是auto类型推导的规则。

    auto可以推导封闭大括号类型为std::initializer_list,但是template不可以。

     

    template<typename Container, typename Index> //C++11的 
    auto                                         // 的最终 
    authAndAccess(Container&& c, Index i)        // 版本
    
    -> decltype(std::forward< Container>(c)[i]) 
    { 
    authenticateUser(); 
    return std::forward<Container>(c)[i]; 
    }
    template<typename Container, typename Index> //C++14的 
    decltype(auto)                               // 最终 
    authAndAccess(Container&& c, Index i)        // 版本 
    { 
    authenticateUser(); 
    return std::forward<Container>(c)[i]; 
    }
     

     

    std::cout << typeid(x).name() << '
    '; // 显示x和y的 
    std::cout << typeid(y).name() << '
    '; // 类型
    这个方法依赖于typeid作用于一个对象上时,返回类型为std::type_info这一个事实,type_info有一个叫name的成员函数,提供了一个C风格的字符串(例如 const char*)来表示这个类型的名字
    template<typename T> 
    void f(const T& param) 
    { 
    using std::cout; 
    cout << "T = " << typeid(T).name() << '
    '; // 显示T的类型 
    cout << "param = " << typeid(param).name() << '
    '; // 显示参数Param的类型  
    }
    GNU和Clang的执行结果是下面这样:
    
    T = PK6Widget 
    param = PK6Widget
    
    我们已经知道PK意味着pointer to const,而6代表了类的名字中有多少个字母(Widget),所以这两个编译器告诉了我们T和param的类型都是const Widget*
    
    Morcrosoft的编译器提供了下面的结果
    
    T = class Widget const * 
    param = class Widget const *
    
    这三个编译器都提供了一样的信息,这或许暗示了结果应该是准确的,但是让我们看的更细致一点,在模板f中,param的类型被声明为constT&,既然如此的话,param和T的类型一样难道不让人感到奇怪吗,如果T的类型是int,param的类型应该是const int&,看,一点都不一样。

     

     

    widget(int i,bool b);
    widget(int i ,double d);
    widget(std::initializer_list<bool> il);

     

    widget w{10,5.0}

     

    {}太厉害,即使上面的有更好的初始化函数匹配,还是会直接尝试调用第三个函数,将10,5.0转换为bool(narrow conversion),但是大括号初始化禁止narrow conversion,报错。

    空大括号表示没有 参数,而不是一个空的std::initializer_list列表。

    widget w5{{}};//这样才能以空列表为参数调用相应构造函数

     

    条款8:prefer nullptr to 0 and NULL

     0和NULL可能被推导成 int或int-like。避免函数重载时0和NULL被推导成 int或int-like。

    nullptr可以隐式转换为任何指针类型

    尤其涉及到模板的时候。

     

    条款10:prefer scoped enums to unscoped enums

    C++98的的enum是unscoped的。新的scoped enums写法:enum class Status;

     scoped enums 可以避免命名空间污染。

    支持前向声明,从而减少依赖关系,不必全部重新编译。

    为什么不需要重新编译呢,因为这种枚举类型有一个默认类型(int),因此在编译时可以确定。当然类型可以修改:

    enum class Status: std::unint32_t

     

     条款11:prefer deleted functions to private undefined ones

    编译器就可以检测到错误而不是链接期。delete成员函数设置为public,这样调用错误可以得到更准确的报错。

    任何函数都可以设置为deleted。可以重载函数并设置为deleted阻止错误类型参数调用。

    还可以将某些函数模板的特化设置为deleted,阻止以这些类型为参数调用该函数。

     

     条款16:make const member functions  thread safe

     简单的互斥计算,std::atomic可能是更好的选择(可能开销更小):

    定义:
    mutable std::atomic<unsigned> callCount{0};
    ……
    用法:
    ++callCount;

    std::mutex、std::atomic等不能复制或移动,因此以它们为成员的类也不能复制或移动。

     

     条款18:use std::unique_ptr for exclusive-ownership resource management

    auto delInvmt = [](Investment* pInvestment) // custom
    {                                                                     // deleter
        makeLogEntry(pInvestment);                    // (a lambda
        delete pInvestment;                                   // expression)
    };
    
    template<typename... Ts> // revised
    std::unique_ptr<Investment, decltype(delInvmt)> // return type
    makeInvestment(Ts&&... params)
    {
        std::unique_ptr<Investment, decltype(delInvmt)> // ptr to be
           pInv(nullptr, delInvmt);                                       // returned
    
        if ( /* a Stock object should be created */ )
        {
            pInv.reset(new Stock(std::forward<Ts>(params)...));
        }
        else if ( /* a Bond object should be created */ )
        {
            pInv.reset(new Bond(std::forward<Ts>(params)...));
        }
        else if ( /* a RealEstate object should be created */ )
        {
           pInv.reset(new RealEstate(std::forward<Ts>(params)...));
        }
        return pInv;
    }

    注意上面std::unique_ptr的第二个参数delInvmt,是一个定制的删除器。另外注意它的reset()函数用法。

    1.std::unique_ptr是一个小巧,迅速,仅能移动(move-only)的灵巧指针,它通过独享所有权的语义来管理资源。
    2.默认情况下,资源释放是通过delete来析构,但是定制删除器可以被用来指定。有状态的删除器和函数指针删除器会增加std::unique_ptr对象的尺寸大小。
    3.std::unique_ptr很容易转换成std::shared_ptr

    c++11 条款19:使用std::shared_ptr来进行共享所有权的资源管理

    auto loggingDel = [](Widget *pw)                   // custom deleter
                                 {                                        // (as in Item 18)
                                     makeLogEntry(pw);
                                     delete pw;
                                 } ;
    std::unique_ptr<                                 // deleter type is
        Widget, decltype(loggingDel)         // part of ptr type
        > upw(new Widget, loggingDel);
    
    
    std::shared_ptr<Widget>                   // deleter type is not
        spw(new Widget, loggingDel);       // part of ptr type

    删除器是std::unique_ptr类型的一部分,但不是std::shared_ptr的一部分。

    另一个和std::unique_ptr不同的地方是,指定一个定制删除器并不会改变std::shared_ptr指针的大小。无论删除器怎样,一个std::shared_ptr内部包含的一定是两个指针。

    一个对象的控制块是由创建第一个指向该对象的std::shared_ptr指针的函数来建立的。至少这是我们预料中的。一般不可能在一个函数创建指向某对象的std::shared_ptr时会知道是否有其他的std::shared_ptr已经指向该对象,所以会用到下面的关于控制块创建的规矩:

    1.std::make_shared(见条款21)总是会创建控制块。该函数创建了一个新对象并指向它,所以当std::make_shared被调用时,当然没有该对象的控制块存在。

    2.当一个std::share_ptr是从一个独享所有权的指针(比如std::unique_ptr或者std::auto_ptr)创建时,会创建控制块。独享所有权的指针不使用控制块,因此被指向的对象也不会有控制块。(作为构建的一部分,这个std::shared_ptr会认为拥有了被指对象的所有权,因此,独享所有权的指针会被置空)。

    3.当std::shared_ptr是通过一个原始指针构造时,它会创建控制块。假如你想通过一个已有控制块的的对象去构造一个std::shared_ptr,你最好传一个std::shared_ptr或者std::weak_ptr(见条款20)作为构造函数的参数,而不是原始指针。std::shared_ptr的构造函数接受std::shared_ptr或者std::weak_ptr作为参数时,并不创建新的控制块,因为它依赖传递进来的灵巧指针已经指向的控制块。

    std::shared_ptr的API提供了这种情况的解决办法。它包含了一个可能在c++标准库里最奇怪的名字:std::enable_shared_from_this。

    它是个基类模板,你如果想一个被std::shared_ptr管理的类能够安全的从this指针来创建成std::shared_ptr,那你可以继承它,Widget继承std::enable_shared_from_this如下:

    class Widget: public std::enable_shared_from_this<Widget> {
    public:
        …
        void process();
        …
    }; 
    
    void Widget::process()
    {
        // as before, process the Widget
    // add std::shared_ptr to current object to processedWidgets
        processedWidgets.emplace_back(shared_from_this());
    }

    在内部,shared_from_this会查找当前对象的的控制块,并且创建一个新的std::share_ptr关联到控制块上。这个设计要依赖于当前对象已经有了一个相应的控制块。

    为此,必须已经存在一个指向当前对象的std::shared_ptr(比如在调用过shared_from_this成员函数之外已经有了一个)。

    假如没有这样一个std::shared_ptr存在(假如当前对象没有相关的控制块存在),那么调用行为将是未知的,尽管 shared_from_this通常会抛异常。

    上面的代码用到了The Curiously Recurring Template Pattern (CRTP) ,奇特的递归基类模板,

    1.std::shared_ptr提供了对共享所有权的任意资源的生命周期的很方便的管理。

    2.和std::unique_ptr相比,std::shared_ptr尺寸增加了一倍,同时因为控制块增加了负担,并且需要原子操作的引用计数。

    3.默认的资源释放是通过delete,但是也支持定制删除器。删除器的类型对std::shared_ptr本身的类型无影响。

    4.避免从原始指针变量去构造std::shared_ptr。

     

     

     

     

     

     

  • 相关阅读:
    New version of VS2005 extensions for SharePoint 3.0
    QuickPart : 用户控件包装器 for SharePoint Server 2007
    随想
    发布 SharePoint Server 2007 Starter Page
    如何在SharePoint Server中整合其他应用系统?
    Office SharePoint Server 2007 中文180天评估版到货!
    RMS 1.0 SP2
    SharePoint Server 2007 Web内容管理中的几个关键概念
    如何为已存在的SharePoint站点启用SSL
    Some update information about Office 2007
  • 原文地址:https://www.cnblogs.com/ph829/p/6958812.html
Copyright © 2011-2022 走看看