【连载】 C++14学习笔记 —— C++编程新时代.

[复制链接]
12326|72
 楼主| dong_abc 发表于 2013-8-8 20:30 | 显示全部楼层 |阅读模式
本帖最后由 dong_abc 于 2013-12-31 20:58 编辑

20131231
一不留神就out了,C++11才刚刚小试牛刀,C++14就来了 。作为虔诚的C/C++派人士,偶会紧跟组织的步伐,时刻保持更新。

虽然已经这么多楼了,不过漏掉的两个重要内容还没补上——右值引用和变长模板。
功力不到家,啃了几次没啃动,要补,补,补......
折腾几个月了,接下来就不是简单的尝试这些知识点了,后面基本应该都是应用了,或许以项目为主题比较好。再看吧......
-----------------------------------------------------------------------------------------------------------------------------------------

20130808
打算深入学一下C++11的新特性。话说C++的重点早已不是面向对象,C++11更是重点突出泛型编程、元编程。

C++不是一种语言,而是一个语言联盟,针对不同的方向,都有不同的应用。

经过10多年的发展,C++11在C++98/03的基础上经过再一次的锤炼,变得更加合理,更加易用,
将再一次推动编程方法的变革。

当然学习难度也更大一些,毕竟内容扩充了,增加了不少新特性以及相应某些特定环境的应用。
甚至提出了一些与传统编程方式迥异的编程方法,这些都是C++经过这些年所沉淀下来的。

程序员应该与时俱进,更新知识库,势在必行!

评分

参与人数 4威望 +19 收起 理由
Ryanhsiung + 6 赞一个!
xlhtracy + 6 很给力!
john_lee + 4 给小草加加油!
refee + 3 赞一个!

查看全部评分

 楼主| dong_abc 发表于 2013-8-8 20:32 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-30 22:02 编辑

第2章  保证稳定性和兼容性

1、C++11关于多态的拓展——final/override控制
1.1  先复习一下多态的基本知识
一个类A中声明的虚函数foo在其派生类B中再次被定义,而且B中的函数foo与A中的函数foo原型一样(函数名、参数列表等一样),
那么我们就称B重载了A中的foo函数。那么此时我们调用B的成员函数就是调用B重载版本。如果同时有A的派生类C,却并没有重载
A的foo函数,那么调用成员函数foo则会调用A中的版本(这段话抄书的,概念我都说不清楚:lol)。

  1. class A
  2. {
  3. public:   
  4.     virtual void foo() =0;
  5.     ...
  6. };

  7. class B: class A
  8. {
  9. public:   
  10.     void foo() ;
  11.     ...
  12. };

  13. class C: class A
  14. {
  15. public:   
  16.     void foo1() ;
  17.     ...
  18. };

以上A类中声明虚函数foo,  B类重载了A的foo函数,此时B类调用成员函数foo则是调用B重载的版本。
而派生类C未重载基类A的foo函数,此时C类调用成员函数则是调用基类A中的版本。
这就是C++所实现的最简单的多态效应。

1.2  看下面例子
  1. class A
  2. {
  3. public:   
  4.     virtual void foo() =0;
  5.     ...
  6. };

  7. class B: class A
  8. {
  9. public:   
  10.     void foo() ;
  11.     ...
  12. };

  13. class C: class B
  14. {
  15. public:   
  16.     void foo() ;
  17.     ...
  18. };

从A派生出B,再从B派生出C;B重载了A的foo函数,C重载了B的foo函数。如果B类是我写的,并且因特殊原因,不能让别人(写C类的人)重载,怎么办?
...
...
在B类的foo函数后面加上final即可阻止函数继续重载。
  1. class B: class A
  2. {
  3. public:   
  4.     void foo()  final; //声明为final
  5.     ...
  6. };

  7. class C: class B
  8. {
  9. public:   
  10.     void foo() ;//无法编译通过
  11.     ...
  12. };

1.3  如果工程中的类结构继承较复杂,那么我们怎么知道其成员函数有没有被重载呢?
为了让类的继承关系清晰明了,C++11引入override关键字,在成员函数后面加上override,则此函数一定是被重载过的。
  1. class C: class B
  2. {
  3. public:   
  4.     void foo() override;//此函数重载了基类B中的foo函数。
  5.     ...
  6. };
至此,C++11关于多态的拓展——final/override控制 部分的 内容完 !
草民 发表于 2013-8-8 22:37 | 显示全部楼层
跟随楼主脚步,关注~

评论

草民  发表于 2013-8-11 11:11
 楼主| dong_abc 发表于 2013-8-10 21:46 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-31 12:20 编辑

2、C++11预定义宏
2.1  _func_ ,在C99中支持_func_ 返回所在函数名的功能。在C++11标准中允许在类/结构体中使用。
  1. class  A
  2. {
  3. public:
  4.           A():name(_func_) {};
  5.           const char *name ;                        
  6. };

2.2   _Pragma
在之前的标准中,我们都是用一下方式来避免头文件重复编译
  1. #ifndef __NUC1xxRtc_H__
  2. #define __NUC1xxRtc_H__
  3. ...
  4. ...
  5. #endif

C++11简化了这一点,用_Pragma(“字符串”)即可
  1. _Pragma (“NUC1xxRtc”);

2.3  静态断言
我们之前都是用assert(...)动态断言来处理,这种方法需要程序运行时才能处理,运行程序需要一些额外的开销,
C++11推出了一种静态断言static_assert,在编译时就能提前处理,不需要运行开销。
  1. #include <iostream>
  2. using namespace std;
  3. template <typename T1, typename T2>
  4. auto add(T1 t1, T2 t2) -> decltype(t1 + t2)
  5. {
  6.     static_assert(std::is_integral<T1>::value, "Type T1 must be integral");
  7.     static_assert(std::is_integral<T2>::value, "Type T2 must be integral");
  8.     return t1 + t2;
  9. }

  10. int main()
  11. {
  12.     //std::cout << add(1, 3.14) << std::endl;
  13.     std::cout << add(111, 2) << std::endl;
  14.     return 0;
  15. }

C++11中还更新了一些其他 宏机制,用得较少,就不关注了。

 楼主| dong_abc 发表于 2013-8-10 22:18 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-11 23:59 编辑

本帖的例程编译环境用的QT5.1(内嵌gcc4.8.0),到目前为止,只有gcc4.8.1支持所有C++11新特性,
所以,如果你也想尝试这些新特性,最好用gcc4.8.1命令行来编译。当然嫌命令行麻烦的话也可以用QT5.1或者VS2012,
只是还有少部分新特性不支持。
 楼主| dong_abc 发表于 2013-8-11 11:56 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-30 21:54 编辑

3、异常——noexcept修饰符
在C++98中,一般都是这么运用异常机制 throw(...)  catch() {...}
  1. #include "stdafx.h"
  2. #include<iostream>
  3. using namespace std;
  4. class A{};
  5. int _tmain(int argc, _TCHAR* argv[])
  6. try{
  7.      int j=0;
  8.      char str[]="hello";
  9.      cout<<"please input a exception nember:";
  10.      int a;
  11.      cin>>a;
  12.      switch(a){
  13.                     case 1: throw A();
  14.                     case 2: throw j;
  15.                     case 3: throw str;
  16.                     default:cout<<"no throws here.\n";
  17.                  }
  18.      cout<<"that 's OK .\n";
  19.      return 0;
  20.      }

  21. catch(int){
  22. cout<<"int is exception.\n";
  23. }
  24. catch(char*){
  25. cout<<"char* is exception.\n";
  26. }
  27. catch(A){
  28. cout<<"class A is exception.\n";
  29. }
像以上这种常规的用法效率较低,实际应用的也很少,在C++11中可以使用noexcept来暴力阻止程序继续运行。
有时候简单粗暴的办法反而更靠谱。
  1. class A{} noexcept; //当case1调用A()时阻止程序继续运行
其实noexcept是调用了std::terminate来中断程序的执行。

 楼主| dong_abc 发表于 2013-8-11 12:43 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-30 21:59 编辑

4、关于类成员初始化
4.1  类成员的初始化看起来很简单,其实这里面还是有些要注意的地方,在我还没看《深入理解C++11》
时,都这样初始化类的。
  1. class A
  2. {
  3. public:   
  4.            foo(const String&) ;
  5. private:
  6.           String name;
  7. };
  8. A::foo(const String& n)
  9. {
  10.    name = n;
  11. }
这里name成员先会被构造函数初始化,然后又被赋值。其实有种显示的初始化语法一步就能完成。
A::foo(const String& n):name(n) {...}

4.2  类成员声明应该与初始化顺序保持一致。
  1. class A
  2. {
  3. public:   
  4.            foo(const String&) ;
  5. private:
  6.           String name;
  7.           int id;
  8. };
  9. A::foo(const String& n):id(0),name(n)  {...} //运行时出错,id和name声明和初始化顺序应该保持一致。

4.3  类成员若是一个引用,则必须初始化。
  1. class A
  2. {
  3. public:   
  4.            foo(const String&) ;
  5. private:
  6.           String& name;
  7.           int id;
  8. };
  9. A::foo(const String& n) {...} //编译时出错,name是一个引用,必须初始化

 楼主| dong_abc 发表于 2013-8-11 13:08 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-11 13:09 编辑

今天周末有空,多写点。第一阶段主要是梳理重要的知识点,折腾这些还是为了写程序时求个 简洁、高效、美观以及良好的拓展性。这些通过新生代C++的泛型编程、元编程模式都可以达到。虽然目前还是编程白痴一个,但是我相信只要像现在这样**学习,很快就可以运用泛型编程、元编程。
refee 发表于 2013-8-11 14:24 | 显示全部楼层
呵呵 支持楼主 :handshake
 楼主| dong_abc 发表于 2013-8-11 14:42 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-11 14:45 编辑
refee 发表于 2013-8-11 14:24
呵呵 支持楼主






@21小跑堂  

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| dong_abc 发表于 2013-8-11 20:11 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-30 21:57 编辑

5、默认的模板参数与外部模板
5.1  C++11中,模板和函数一样,可以有默认的参数。
  1. template <typename T = int>
  2. class  A {...}; // void A() {...};类模板和函数模板都支持

虽然模板像函数一样支持默认参数,但是有多个模板参数的时候还得遵循“从右往左”的规则。
  1. template <typename T1 = int,typename T2> class A; //编译出错
  2. template <typename T1 ,typename T2 = int> class A; //编译通过
  3. template <typename T1 = int,typename T2 = int> class A; //编译通过
以上都是针对类模板,函数模板参数的位置就没有此限制了。
  1. template <typename T1 = int,typename T2> void A(T1 a,T2 b); //编译通过
SO,类模板参数需要遵循“从右往左”的规则。

5.2  外部模板
我们在test.h中声明一个模板函数

  1. template <typename T > void A(T) {}
在test1.cpp中调用A函数A(1);
在tes21.cpp中调用A函数A(2);
此时编译器实例化出了两个A<int>(int)副本;C++11为了改进这一点,引进了“外部模板”这个概念。

以上实例,我们只要在test1.cpp中做显示实例化
  1. template void A<int>(int);
然后在test2.cpp中声明外部模板,
  1. extern template void A<int>(int);
这样我们在多个文件中调用void A(int)函数编译器就只保留一个A<int>(int)副本。
从这一点可以看出,外部模板 和 外部变量 的使用方式如出一辙 !
 楼主| dong_abc 发表于 2013-8-11 20:59 | 显示全部楼层
C++还有巨多给力的地方,限于本人基础较薄弱,目前还使不出C++的威力。

接下来更精彩!
天凉好个秋 发表于 2013-8-11 21:38 | 显示全部楼层
关注中
天凉好个秋 发表于 2013-8-11 21:38 | 显示全部楼层
今天是11号,看C++11最适合
 楼主| dong_abc 发表于 2013-8-11 22:09 | 显示全部楼层
天凉好个秋 发表于 2013-8-11 21:38
关注中


就算没有一个人关注,本帖也会连载到底,第一阶段主要是熟悉C++11强大的新特性阵容,后一阶段就是将这些新的编程方式牵引到单片机/ARM/DSP等处理器上。
aihe 发表于 2013-8-11 23:19 | 显示全部楼层
正在学习中,还没看到所说部分,先顶着,回头慢慢品
123de7 发表于 2013-8-12 11:03 | 显示全部楼层
也正在学习中  不过  好吧  我承认我是菜鸟
51armfpga 发表于 2013-8-13 09:03 | 显示全部楼层
支持楼主,加油!!!!!
jlass 发表于 2013-8-13 09:17 | 显示全部楼层
顶一下,虽然看不懂。
 楼主| dong_abc 发表于 2013-8-13 18:37 | 显示全部楼层
本帖最后由 dong_abc 于 2013-8-30 22:01 编辑

第3章   通用为本,专用为末

1、关于构造函数 ——继承构造函数/委派构造函数

1.1  继承构造函数。
我们可以将基类的构造函数通过using关键字非常简洁的继承下来为我所用。
  1. struct A {   
  2. void f(int i){}   
  3. void f(int i,double d){}
  4. };

  5. struct B:A{   
  6. using A::f;  //用using来声明基类中的成员函数。   
  7. void f(const char* s){}
  8. };

  9. int main(){
  10. B b;  
  11. b.f(1);
  12. b.f(1,1.1);
  13. //基类的构造被继承下来,都可以被派生类使用。 注意,不仅仅是构造函数  
  14. //可以继承下来使用,普通成员函数也可以的!
  15. }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:此id已冬眠...

43

主题

5073

帖子

22

粉丝
快速回复 在线客服 返回列表 返回顶部