C++11 新标准(二)

来源:互联网 发布:可以画画的软件 编辑:程序博客网 时间:2024/05/19 14:39

1.构造函数的改良

(1)C++11允许构造函数调用其他的构造函数,这中做法叫做委托或转接(delegation)如:
class SomeType{public:    SomeType():SomeType(0,"invalid"){}    SomeType(int i):SomeType(i,"guess"){}    SomeType(string& s):SomeType(1,s){}private:    int number;    string name;    SomeType(int i,string& s):number(i),name(s){}};
(2)C++11允许派生类手动继承基类的构造函数,编译器可以使用基类的构造函数完成派生类的构造:
class Base{public:    Base(int i);};class Derived{public:    using Base::Base;};
该语法相当于Derived声明了一个Derived(int)的构造函数
(3)另一方面,C++11允许使用一下的语法完成成员初始化
class Type{public:    Type(int i);private:    int iValue = 5;};
如果构造函数中没有设置iValue的初始化,则会采用类定义中的成员初始化

2.显式虚函数重载

在 C++ 里,在子类中容易意外的重载虚函数。举例来说:
struct Base {    virtual void some_func();}; struct Derived : Base {    void some_func();};
一方面,some_func()函数是否是为了重载不好确定,另一方面,当基类中的虚函数的签名被改变,子类中拥有旧签名的函数就不再重载该虚函数。因此,如果程序员忘记修改所有子类,运行期将不会正确调用到该虚函数正确的实现
C++11 将加入支持用来防止上述情形产生,并在编译期而非运行期捕获此类错误。为保持向后兼容,此功能将是选择性的
struct Base {    virtual void some_func(float);}; struct Derived : Base {    virtual void some_func(int) override;   // 错误格式: Derive::some_func 并沒有 override Base::some_func    virtual void some_func(float) override; // 重载正确};
编译器会检查基底类型是否存在一虚拟函数,与派生类中带有声明override 的虚拟函数,有相同的函数签名(signature);若不存在,则会回报错误
C++11 也提供指示字final,用来避免类型被继承,或是基底类型的函数被改写:
struct Base1 final { }; struct Derived1 : Base1 { }; // 错误格式: class Base1 已标明为 final struct Base2 {    virtual void f() final;}; struct Derived2 : Base2 {    void f(); // 错误格式: Base2::f 已标明为 final};

3.空指针

C++ 并不采用 C 的规则,不允许将 void* 隐式转换为其他类型的指针。 为了使代码 char* c = NULL; 能通过编译,NULL 只能定义为0。 这样的决定使得函数重载无法区分代码的语义:
void foo(char *);
void foo(int);
C++ 建议 NULL 应当定义为 0,所以foo(NULL); 将会调用 foo(int), 这并不是程序员想要的行为,也违反了代码的直观性。0 的歧义在此处造成困扰
C++11 引入了新的关键字来代表空指针常数:nullptr,将空指针和整数 0 的概念拆开。 nullptr 的类型为nullptr_t,能隐式转换为任何指针或是成员指针的类型,也能和它们进行相等或不等的比较。 而nullptr不能隐式转换为整数,也不能和整数做比较
为了向下兼容,0 仍可代表空指针常数
char* pc = nullptr;     // OKint * pi = nullptr;     // OKint    i = nullptr;     // errorfoo(nullptr);           // 调用 foo(char *)

4.枚举类——强枚举类型

(1)枚举类(“新的枚举”/“强类型的枚举”)主要用来解决传统的C++枚举的三个问题:
传统C++枚举会被隐式转换为int,这在那些不应被转换为int的情况下可能导致错误
传统C++枚举的每一枚举值在其作用域范围内都是可见的,容易导致名称冲突(同名冲突)
不可以指定枚举的底层数据类型,这可能会导致代码不容易理解、兼容性问题以及不可以进行前向声明
(2)枚举类(enum)(“强类型枚举”)是强类型的,并且具有类域:
enum Alert { green, yellow, election, red }; // 传统枚举enum class Color { red, blue };   // 新的具有类域和强类型的枚举类 // 它的枚举值在类的外部是不可直接访问的,需加“类名::” //  不会被隐式转换成int //   enum class TrafficLight { red, yellow, green }; Alert a = 7;   //  错误,传统枚举不是强类型的,a没有数据类型 Color c = 7;   // 错误,没有int到Color的隐式转换 int a2 = red;           // 正确,Alert被隐式转换成intint a3 = Alert::red;    // 在 C++98中是错误的,但是在C++11中正确的int a4 = blue;            // 错误,blue并没有在类域中int a5 = Color::blue; // 错误,没有Color到int的默认转换Color a6 = Color::blue;   // 正确
(3)因为可以指定枚举的底层数据类型,所以可以进行简单的互通操作以及保证枚举值所占的字节大小:
enum class Color : char { red, blue }; // 紧凑型表示(一个字节)// 默认情况下,枚举值的底层数据类型为intenum class TrafficLight { red, yellow, green };// E占几个字节呢?旧规则只能告诉你:取决于编译器实现enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U };// C11中我们可以指定枚举值的底层数据类型大小enum EE : unsigned long { EE1 = 1, EE2 = 2, EEbig = 0xFFFFFFF0U };
(4)同时,由于能够指定枚举值的底层数据类型,所以前向声明得以成为可能:
enum class Color_code : char;     //(前向)声明void foobar(Color_code* p);   //使用// ...//定义enum class Color_code : char { red, yellow, green, blue };
枚举类的底层数据类型必须是有符号或无符号的整型,默认情况下是int

5.显式类型转换子

标准C++中对构造函数引入了explicit关键字,避免用户自定义的单引数构造函数变成隐式转换子,但是在类型转换子上,却没有任何着墨
C++11中对类型转换子同样引入了explicit关键字,避免隐式转化,如:
//没有explicit关键字#include <iostream>using namespace std;class Someclass{public:    Someclass(){        b = new int(1);    }    ~Someclass(){        if(b != NULL){            delete b;        }    }    operator bool(){        return b;    }private:    int *b;};int main(){    Someclass t;    //判断t管理的指针是否问nullptr,是我们需要的功能    if(t){        cout << "true\n";    }    //bool也算算数类型,能隐式转化为整数甚至是浮点数,这不是我们需要的功能     cout << 1 + t << endl;    return 0;}/*输出结果为:true2 */

//添加explicit关键字#include <iostream>using namespace std;class Someclass{public:    Someclass(){        b = new int(1);    }    ~Someclass(){        if(b != NULL){            delete b;        }    }    explicit operator bool(){        return b;    }private:    int *b;};int main(){    Someclass t;    //判断t管理的指针是否问nullptr,是我们需要的功能    if(t){        cout << "true\n";    }    //[Error] no match for 'operator+' (operand types are 'int' and 'Someclass')    cout << 1 + t << endl;    return 0;}

6.模板别名(template typedef)

C++中,不能用typedef来定义模板的别名,如:
template <typename first,typename second,int third>class SomeType{};typedef SomeType<int,int,5> Typename1;      //正确 template<typename second>typedef SomeType<int, second,5> Typename2;  // [Error] template declaration of 'typedef'

为了定义模板的别名,C++增加一下语法:using 模板别名 = 引用细节
template <typename first,typename second,int third>class SomeType{};template<typename second>using  Typename =  SomeType<int, second,5>; // 正确
除了在模板方面身担重任外,using语法也可作为对普通类型定义别名的另一种选择
typedef void (*PFD)(double);    // C 样式using PF = void (*)(double);    // using加上C样式的类型using P = [](double)->void;  // using和函数返回类型后置语法

7.无限制的union

在标准 C++ 中,并非任意的类型都能做为 union 的成员。比方说,带有 non-trivial 构造函数的类型就不能是 union 的成员。在新的标准里,移除了所有对 union 的使用限制,除了其成员仍然不能是引用类型。
补充:non-trivial
trivial意思是无意义,这个trivial和non-trivial是对类的四种函数来说的:
构造函数(ctor)
复制构造函数(copy)
赋值函数(assignment)
析构函数(dtor)


如果满足下面3条里的一条:
显式(explict)定义了这四种函数
类里有非静态非POD的数据成员
有基类
那么上面的四种函数是non-trivial函数,也就是说有意义的函数

0 0