C++11中对类(class)新增的特性

C++11中对类(class)新增的特性

default/delete 控制默认函数

在咱们没有显式定义类的复制构造函数和赋值操做符的状况下,编译器会为咱们生成默认的这两个函数:
默认的赋值函数之内存复制的形式完成对象的复制。
这种机制能够为咱们节省不少编写复制构造函数和赋值操做符的时间,可是在某些状况下,好比咱们不但愿对象被复制,
在以前咱们须要将复制构造函数和赋值操做符声明为private,如今可使用delete关键字实现:html

class X {
    // …
    X& operator=(const X&) = delete;   // 禁用类的赋值操做符
    X(const X&) = delete;
};

显式地使用default关键字声明使用类的默认行为,对于编译器来讲明显是多余的,可是对于代码的阅读者来讲,使用default显式地定义复制操做,则意味着这个复制操做就是一个普通的默认的复制操做。git

override /final 强制重写/禁止重写虚函数

派生类中能够不实现基类虚函数,也能够实现,但不使用virtual关键字;
这很容易给人形成混淆,有时为了确认某个函数是不是虚函数,咱们不得不追溯到基类查看;
C++11引入了两个新的标识符: override和final
override,表示函数应当重写基类中的虚函数。(用于派生类的虚函数中)
final,表示派生类不该当重写这个虚函数。(用于基类中)github

struct B {
       virtual void f();
       virtual void g() const;
       virtual void h(char);
       void k();      // non-virtual
       virtual void m() final; 
};
 
struct D : B {
       void f() override;     // OK: 重写 B::f()
       void g() override;     // error: 不一样的函数声明,不能重写
       virtual void h(char);  // 重写 B::h( char ); 可能会有警告
       void k() override;     // error: B::k() 不是虚函数
       virtual void m();       // error: m()在基类中声明禁止重写
};

有了这对兄弟,咱们的虚函数用起来更为安全,也更好阅读;安全

委托构造函数 Delegating constructors

在C++98中,若是你想让两个构造函数完成类似的事情,能够写两个大段代码相同的构造函数,或者是另外定义一个init()函数,让两个构造函数都调用这个init()函数。例如:ide

class X {
        int a;
        // 实现一个初始化函数
        validate(int x) {
            if (0<x && x<=max) a=x; else throw bad_X(x);
        }
    public:
        // 三个构造函数都调用validate(),完成初始化工做
        X(int x) { validate(x); }
        X() { validate(42); }
        X(string s) {
            int x = lexical_cast<int>(s); validate(x);
        }
        // …
    };

这样的实现方式重复罗嗦,而且容易出错。
在C++0x中,咱们能够在定义一个构造函数时调用另一个构造函数:函数

class X {
        int a;
    public:
        X(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); }
        // 构造函数X()调用构造函数X(int x)
        X() :X{42} { }
        // 构造函数X(string s)调用构造函数X(int x)
        X(string s) :X{lexical_cast<int>(s)} { }
        // …
    };

继承的构造函数 Inheriting constructors

C++11提供了将构造函数晋级的能力:
好比如下这个示例,基类提供一个带参数的构造函数,而派生类没有提供;
若是直接使用D1 d(6);将会报错;经过将基类构造函数晋级,派生类中会隐式声明构造函数D1(int);
须要注意的是,晋级后的基类构造函数是没法初始化派生类的成员变量的,因此若是派生类中有成员变量,
须要使用初始化列表初始化;加密

struct B1 {
        B1(int) { }
    };
    struct D1 : B1 {
        using B1::B1; // 隐式声明构造函数D1(int)
        // 注意:在声明的时候x变量已经被初始化
        int  x{0};
    };
    void test()
    {
        D1 d(6);    // d.x的值是0
    }

类内部成员的初始化 Non-static data member initializers

在C++98标准里,只有static const声明的整型成员能在类内部初始化,而且初始化值必须是常量表达式。这些限制确保了初始化操做能够在编译时期进行。.net

class X {
    static const int m1 = 7;   // 正确
    const int m2 = 7;    // 错误:无static
    static int m3 = 7;              // 错误:无const
    static const string m5 = “odd”; //错误:非整型
};

C++11的基本思想是,容许非静态(non-static)数据成员在其声明处(在其所属类内部)进行初始化。这样,在运行时,须要初始值时构造函数可使用这个初始值。如今,咱们能够这么写:code

class A {
public:
    int a = 7;
};
它等同于使用初始化列表:
class A {
public:
    int a;
    A() : a(7) {}
};

单纯从代码来看,这样只是省去了一些文字输入,但在有多个构造函数的类中,其好处就很明显了:htm

class A {
    public:
         A(): a(7), b(5), hash_algorithm(“MD5″),
           s(“Constructor run”) {}
        A(int a_val) :
          a(a_val), b(5), hash_algorithm(“MD5″),
          s(“Constructor run”)
          {}
        A(D d) : a(7), b(g(d)),
            hash_algorithm(“MD5″), s(“Constructor run”)
            {}
        int a, b;
    private:
        // 哈希加密函数可应用于类A的全部实例
        HashingFunction hash_algorithm;
        std::string s;  // 用以指明对象正处于生命周期内何种状态的字符串
    };

能够简化为:

class A {
    public:
        A() {}
        A(int a_val) : a(a_val) {}
        A(D d) : b(g(d)) {}
        int a = 7;
        int b = 5;
    private:
        //哈希加密函数可应用于类A的全部实例
        HashingFunction hash_algorithm{“MD5″};
        //用以指明对象正处于生命周期内何种状态的字符串
        std::string s{“Constructor run”};

多么优雅!

移动构造和移动赋值

在C++98中,咱们自定义的类,会默认生成拷贝赋值操做符函数和拷贝赋值函数以及析构函数;
在C++11中,依赖于新增的move语义,默认生成的函数多了2个移动相关的:移动赋值操做符( move assignment )和移动构造函数( move constructor );

BS建议,若是你显式声明了上述 5 个函数或操做符中的任何一个,你必须考虑其他的 4 个,而且显式地定义你须要的操做,或者使用这个操做的默认行为。

一旦咱们显式地指明( 声明 , 定义 , =default , 或者 =delete )了上述五个函数之中的任意一个,编译器将不会默认自动生成move操做。
一旦咱们显式地指明( 声明 , 定义 , =default , 或者 =delete )了上述五个函数之中的任意一个,编译器将默认自动生成全部的拷贝操做。可是,咱们应该尽可能避免这种状况的发生,不要依赖于编译器的默认动做。

若是你声明了上述 5 个默认函数中的任何一个,强烈建议你显式地声明全部这 5 个默认函数。例如:

template<class T>
class Handle {
    T* p;
public:
    Handle(T* pp) : p{pp} {}
    // 用户定义构造函数: 没有隐式的拷贝和移动操做
        ~Handle() { delete p; }
    Handle(Handle&& h) :p{h.p}
        { h.p=nullptr; }; // transfer ownership
    Handle& operator=(Handle&& h)
        { delete p; p=h.p; h.p=nullptr; } // 传递全部权
    Handle(const Handle&) = delete;  // 禁用拷贝构造函数
    Handle& operator=(const Handle&) = delete;
    // ...
};

参考

http://www.stroustrup.com/C++11FAQ.html
https://www.chenlq.net/books/cpp11-faq

Posted by: 大CC | 02SEP,2015
博客:blog.me115.com [订阅]
Github:大CC

相关文章
相关标签/搜索