深入理解C++11

更新时间:2023-05-20 20:45:31 阅读: 评论:0

深⼊理解C++11
⽂章⽬录
整理  <<;深⼊理解C++11 C++11新特性解析与应⽤>> 笔记
2018年春晚第2章 保证稳定性和兼容性
final/override 控制
局部和匿名类型作模板实参struct  Object {    virtual  void  fun () = 0;}struct  Ba : public  Object {    void  fun () final ; // 声明为final }struct  Derived : public  Ba {    void  fun (); // ⽆法通过编译}
1
2
3
4
5
6
7
8
9struct  Ba {  virtual  void  Turing () = 0;  virtual  void  Dijkstra () = 0;  virtual  void  VNeumann (int  g ) = 0;  virtual  void  DKnuth () = 0;  void  Print ();}struct  DerivedMid : public  Ba {  // void VNeumann(double g);  // 接⼝被隔离,曾想多⼀个版本的VNeumann 函数}struct  DerivedTop : public  DerivedMid {  void  Turing () override ;  void  Dikjstra () override ;  // ⽆法通过编译, 拼写错误,⾮重载  void  VNeumann (double  g ) override ;  // ⽆法通过编译, 参数不⼀致,⾮重载  void  DKnuth () const  override ;  // ⽆法通过编译,常量性不⼀致,⾮重载  void  Print () override ;  // ⽆法通过编译, ⾮虚函数重载}
1
2
3
4
5
6
7
8
9
10
11
12
会务是什么工作13
14
15
16
17
18
19
20
但是不能就地定义, 使⽤decltype获取类型
第3章 通⽤为本,专⽤为末
继承构造函数
情景: 在派⽣类中我们写的构造函数完完全全是为了构造基类,⽽基类有很多构造函数,那么我们需要为派⽣类写很多的"透传"构造函数.c++11 使⽤using声明继承基类的构造函数来解决这个重复写的问题.通过using A::A 的声明,把基类中的构造函数都继承到派⽣类B中.⽽且这是隐式声明的,意味这如果没有实际调⽤,编译器不会⽣成相关代码,节省了⽬标代码空间.
有的时候,会发⽣继承构造函数冲突问题,需要显⽰定义,阻⽌隐式⽣成相应的构造函数来解决冲突
注意:template <typename  T > class  X {};template <typename  T > void  TempFun (T  t ) {}struct  A {} a ;struct  { int  i ; } b ; // 匿名类型typedef  struct  { int  t ; } B ;void  Fun () {    struct  C {} c ;  // 局部类型    X <A > x1;    // c++98 通过, c++11 通过    X <B > x2;    // c++98 错误, c++11 通过    X <C > x3;    // c++98 错误, c++11 通过    TempFun (a ); // c++98 通过, c++11 通过    TempFun (b ); // c++98 错误, c++11 通过    TempFun (c ); // c++98 错误, c++11 通过}
1
2
34
埃及旅游5
6
7
8
9
10
11
12
购物天堂
13
14MyTemplate <struct  { int  a ; } > t ; // 编译错误,不能就地定义struct  {    int  a ;}A ;MyTemplate <decltype (A )> t ; // 编译通过
1
2
3
4
5
6struct  A {    A (int  i ) {}    A (double  d , int  i ) {}    A (float  f , int  i , const  char * c ) {}    // ...};struct  B : A {    using  A ::A ; // 继承构造函数    // ...    virtual  void  ExtraInterface () {}};
1
2
3
4
5
6
不要说你不知道
7
8
9
10
11struct  A { A (int ) {} };struct  B { B (int ) {} };struct  C : A , B {    using  A ::A ;    using  B ::B ;    C (int ) {} // 显式定义}
1
2
3
4
5
6
7
8
1. 基类的构造函数是私有函数,不能继承
2. 使⽤了继承构造函数,那么不再为派⽣类⽣成默认构造函数.
委派构造函数
委派构造,就是指委派函数将构造的任务委派给委派构造函数(delegating constructor). 构造函数不能同时"委派"和使⽤初始化列表.初始化列表的初始化⽅式总是先于构造函数完成(实际在编译完成时已经决定了).
可以形成委派链,但是不能有委派环.
委派函数实际应⽤是使⽤构造模板函数产⽣⽬标构造函数, 委托构造函数使得泛型编程成为⼀种可能
右值引⽤:移动语义和完美转发
移动语义class  Info {    public :    Info (): Info (1, 'a') {}    Info (int  i ): Info (i , 'a') {}    Info (char  e ): Info (1, e ) {}    private :    Info (int  i , char  e ): type (i ), name (e ) {}    int  type ;    char  name ;}
1
2
3
4
5
6
7
8
9
10
11#include  <iostream>#include  <deque>#include  <vector>using  namespace  std ;class  TDConstructed {    template <class  T > TDConstructed (T first , T cond ): l (first , last ) {}    list <int > l ; public :    TDConstructed (vector <short >& v ): TDConstructed (v .begin (), v .end ()) {}    TDConstructed (deque <int >& d ): TDConstructed (d .begin (), d .end ()) {}};
1
2
3
4
5
6
7
8
9
10
11
12
调⽤过程
添加移动构造函数
右值和左值的区别
可以取地址,有名字的是左值.不能取地址的,没有名字的是右值.
在C++11程序中,所有值必须属于左值,纯右值,将亡值三者之⼀.右值分为:纯右值,将亡值
纯右值: ⽤于辨认临时变量和⼀些不跟对象关联的值,如 1 + 3 产⽣的临时变量值
将亡值: 是C++11新增的跟右值引⽤相关的表达式.如T&&的函数返回值.#include  <iostream>using  namespace  std ;class  HasPtrMem { public :    HasPtrMem (): d (new  int (0)) {        cout << "Construct: " << ++n_cstr << endl ;    }    HasPtrMem (const  HasPtrMem & h ): d (new  int (*h .d )) {        cout << "Copy construct: " << ++n_cptr << endl ;    }    ~HasPtrMem () {        cout << "Destruct: " << ++n_dstr << endl ;    }    int  *d ;    static  int  n_cstr = 0;    static  int  n_cptr = 0;    static  int  n_dstr = 0;};HasPtrMen getTemp () { return  HasPtrMen (); }int  main () {    HasPtrMen a = getTemp ();}// c++ -std=c++11 test.cpp -fno-elide-constructors // 需要关掉编译器返回值优化选项(RVO, Return Value Optimization)
1
2
3
4
5
6
7
党员的好处
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27Construct: 1Copy construct: 1Destruct: 1Copy construct: 2Destruct: 2Destruct: 3
1
2
3
4
5
6graph TD GetTemp(( ))--构造-->HasPrtMem HasPrtMem--拷贝构造-->HasPtrMem 临时对象HasPtrMem 临时对象--拷贝构造-->a
1
2
3
4    HasPtrMem (HasPtrMem && h ): d (h .d ) {        h .d = nullptr ;  // 将临时值的指针成员置空        cout << "Construct: " << ++n_mvtr << endl ;    }
1
蝴蝶手工
2工作经验英文
3
4T ReturnRvalue () {}T && a = ReturnRvalue (); // 调⽤移动构造函数,为临时对象续命
1
2
注意: 右值引⽤不能绑定到左值上
注意 常量左值引⽤是⼀个"万能"的存在,只读不能修改
std::move 强制转化为右值std::move 从实现上讲,基本等同与⼀个类型转换static_cast<T&&>(lvalue
std::move 转化的左值不会⽴刻被析构
move() 使⽤场景
在移动构造函数中,为了保证成员变量也能使⽤移动构造函数,需要⽤到move()
移动语义其他问题上⾯的⽣命都会使得右值常量化,那么对临时变量的修改不能进⾏,⽆法实现移动语义.
在C++11中, 拷贝构造/赋值构造/移动构造/移动赋值 函数必须同时提供,或者同时不提供. ⼀旦提供⼀个,编译器将不会隐式⽣成其他三个.对与移动语义,抛异常是危险的,因为可能移动语义未完成,异常抛出,导致⼀些指针成悬挂指针,因此尽量写不抛出异常的移动构造函数,⼀旦有异常,直接终⽌程序, 添加 noexcept  关键字
完美转发int  a ;int && b = a ; // 编译失败
1
2T ReturnRvalue () {}T & e = ReturnRvalue ();          // 编译失败const  T & f = ReturnRvalue ();    // 编译通过T && g = ReturnRvalue ();        // 编译通过
1
2
3
4
5#include  <iostream>class  Moveable { public :    Moveable (): i (new  int (3)) {}    ~Moveable () { delete  i ; }    Moveable (const  Moveable & m ): i (new  int (*m .i )) {}    Moveable (Moveable && m ): i (m .i ) { m .i = nullptr ; }    int * i ;};int  main () {    Moveable a ;    Moveable c (move (a ));    // 调⽤移动构造函数    cout << *a .i << endl ;  // 运⾏错误}
1
2
3
4
5
6
7
8
9
10
11
12
13
14class  Moveable { public :    Moveable (Moveable && m ): i (m .i ), h (move (m .h )) {        m .i = nullptr ;    }    int * i ;    HugeMem h ;}
1
2
3
4
5
6
7
8Moveable (const  Moveable &&);const  Moveable ReturnVal ();
1
2

本文发布于:2023-05-20 20:45:31,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/711568.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:右值   移动   构造   不能   左值   函数   委派   编译
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图