【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理

目录

 

2-1.打印标准库等信息【2-1-1.cpp】

2-2.利用宏定义__func__打印文件名【2-1-2.cpp】

2-3.C++11运行__func__用于类和结构体中

2-4-1.一个简单的变长参数宏的应用

2-4-2.用__cpluscplus宏来强制使用户使用支持C++11及以上的编译器来编译【2-4-1.cpp】

2-5.C++11中用宏定义查看long long类型相关的值

2-6.assert宏用于运行时断言-演示报错【2-5-1.cpp】

2-7.assert宏用于运行时断言示例-演示一个编译器对象支持的编译风格的“静态断言”【2-5-2.cpp】

2-8. assert宏用于运行时断言示例-演示两种数据类型的长度保证一致的“静态断言”【2-5-3.cpp】

2-9-1.用“除0”这种静态断言实现在模版实例化的时候的编译器的报错【2-5-4-1.cpp】

2-9-2.用static_assert实现编译时产生清晰的报错【2-5-4-2.cpp】

2-10.static_assert可以用于任何命名(名字)空间--简单示例【2-5-5.cpp】

2-11.static_assert的断言表达式必须时可以计算的表达式(常量表达式),变量会出错【2-5-6.cpp】

2-12. 被noexcpt修饰的函数一旦抛异常,编译器就会直接调用std::terminate()来终止程序的运行【2-6-1.cpp】

2-13.C++11中类的构造和析构函数默认不抛异常,除非显示指定或类的基类或成员有noexcept(false)的析构函数

2-14.C++98的静态成员常量的“就地”声明中的限制

2-15.C++11中使用就地圆括号的表达式列表初始化非静态成员会导致编译出错【2-7-2.cpp】

2-16 .C++11中用各种手段初始化非静态成员【2-7-3.cpp】

2-17. 多个构造函数且有多个成员变量的时候可以看出C++11新方式带来的便利

2-18.书中在C++98标准中的sizeof不能对非静态成员使用,但为啥我用的C++98标准的时候的编译器是能通过的呢?【2-8-1.cpp】

2-19.书中说C++98在什么友元类的时候需要加class关键字,为啥我的98不加也可以呢?

2-20.利用2-19中的变化为类模板声明友元【2-9-2.cpp】

2-21.利用危险方法且不优雅的方法写出测试程序【2-9-3.cpp】

2-22.针对2-21的缺陷,利用类模板与友元结合实现良好的封装代码-【2-9-4.cpp】

2-23.基类的成员函数fun被声明为virtual的时候,对于其派生类来说,fun总是可以被重写覆盖的【2-10-1.cpp】

2-24.final关键字使得派生类不可以通过重写来覆盖它所修饰的虚函数

2-25-1.虚函数描述符override可以帮我们的编译器做一些辅助检查

2-25-2.一个正确使用override的示例

2-26.一个简单的例子回顾一下函数模板【2-11-1-1.cpp】

2-27.C++98标准不支持函数模板的默认模板参数,C++11中开始支持函数模板的默认模板参数【2-11-1-2.cpp】

2-28.C++11中类模板的默认参数必须遵循从右至左的规则,函数模板的默认参数指定没有规则【2-11-2.cpp】

2-29.模板函数的默认形参不是模板参数推导的依据,函数模板参数的选择,总是由函数的实参推导出来的

2-30.C++11针对C++98中的局部和匿名类型不能作为模板类的实参做出调整【2-13-1.cpp】


 

2-1.打印标准库等信息【2-1-1.cpp】

nclude 
using namespace std;
int main()
{
    cout << "Standard Clib:"<<__STDC_HOSTED__<

2-2.利用宏定义__func__打印文件名【2-1-2.cpp】

#include 
#include 
using namespace std;
const char* hello(){
  return __func__;
}
const char* world(){
  return __func__;
}
  int main(){
  cout << hello()<<","<

2-3.C++11运行__func__用于类和结构体中

#include 
using namespace std;
struct TestStruct{
       TestStruct(): name(__func__){}
       const char *name;
};
int main(){
       TestStruct ts; 
       cout<< ts.name <

2-4-1.一个简单的变长参数宏的应用

#include 
#define LOG(...){\
   fprintf(stderr,"%s:Line %d:\t",__FILE__,__LINE__);\
   fprintf(stderr,__VA_ARGS__);\
   fprintf(stderr,"\n");\
}
int main(){
   int x = 3;
   // ....
   LOG("x=%d",x);
   return 0;
}

2-4-2.用__cpluscplus宏来强制使用户使用支持C++11及以上的编译器来编译【2-4-1.cpp】

#include 
#include 
#if __cplusplus < 201103L
   #error "should use C++11 implementation"
#endif
int main()
{
  printf("test\n");
}

本例子还可以用作编译时断言的测试代码.

2-5.C++11中用宏定义查看long long类型相关的值

#include 
#include 
#ifdef __cplusplus 
   #define DD 900
#endif 
using  namespace std;
int main(){
    long long ll_min = LLONG_MIN;
    long long ll_max = LLONG_MAX;
    unsigned long long ull_max = ULLONG_MAX;
    printf("min of long long: %lld\n",ll_min);
    printf("max of long long: %lld\n",ll_max);
    printf("max of unsigned long long: %llu,ULLONG_MAX:%llu\n",ull_max,ULLONG_MAX);
    printf("DD is %d\n",DD);
    return 0;
}

2-6.assert宏用于运行时断言-演示报错【2-5-1.cpp】

#include 
using namespace std;
char * ArrayAlloc(int n){ 
   assert(n>0);//assert that n must be greater than 0
   return new char [n];
}
int main(){
   char* a = ArrayAlloc(0);
   return 0;
}

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第1张图片

2-7.assert宏用于运行时断言示例-演示一个编译器对象支持的编译风格的“静态断言”【2-5-2.cpp】

#include 
#include 
using namespace std;

enum FeatureSupports{
  C99   = 0x0001,
  ExInt = 0x0002, 
  SAssert = 0x0004,
  NoExcept = 0x0008,
  SMAX = 0x0010,
};

struct Compiler{
  const char * name;
  int spp;
};

int main(){
   // check if the all enums are here 
   assert((SMAX-1)==(C99 | ExInt | SAssert | NoExcept));
   Compiler a = {"abc",(C99 | SAssert)};
   //...
   if(a.spp & C99){
      printf("a.spp = %d , C99 = %d ,test line = %d, compiler %s supports C99.\n",a.spp,C99,__LINE__,a.name);
   }else{
      printf("a.spp = %d , C99 = %d ,test line = %d, compiler %s does not support C99.\n",a.spp,C99,__LINE__,a.name);
   }   
   if(a.spp & SMAX){
      printf("a.spp = %d , SMAX = %d , test line = %d,compiler %s supports SMAX \n",a.spp,SMAX,__LINE__,a.name);
   }else{
      printf("a.spp = %d , SMAX = %d , test line = %d,compiler %s does not support SMAX. \n",a.spp,SMAX,__LINE__,a.name);
   }   
   return 0;

}

2-8. assert宏用于运行时断言示例-演示两种数据类型的长度保证一致的“静态断言”【2-5-3.cpp】

#include 
#include 
using namespace std;
template
int bit_copy(T&a ,U&b){
    assert(sizeof(b)== sizeof(a));
    memcpy(&a,&b,sizeof(b));
};
int main(){
   int a = 0x2468;
   double b;
   bit_copy(a,b);
   return 0;
}

2-9-1.用“除0”这种静态断言实现在模版实例化的时候的编译器的报错【2-5-4-1.cpp】

#include 
using namespace std;
#define assert_static(e)\
do{\
   enum{assert_static__ = 1/(e)};\
} while(0)

template int bit_copy(T&a ,U&b)
{
    assert_static(sizeof(b)== sizeof(a));
    memcpy(&a,&b,sizeof(b));
};
int main(){
   int a = 0x2468;
   double b;
   bit_copy(a,b);
   return 0;
}

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第2张图片

2-9-2.用static_assert实现编译时产生清晰的报错【2-5-4-2.cpp】

#include 
using namespace std;
template int bit_copy(T&a ,U&b)
{
    static_assert(sizeof(b)== sizeof(a),"the parameters of bit_copy must have same width");
    memcpy(&a,&b,sizeof(b));
};
int main(){
   int a = 0x2468;
   double b;
   bit_copy(a,b);
   return 0;
}

2-10.static_assert可以用于任何命名(名字)空间--简单示例【2-5-5.cpp】

static_assert(sizeof(int) == 8,"This 64-bit machine should follow this!");
int main(){return 0;}

2-11.static_assert的断言表达式必须时可以计算的表达式(常量表达式),变量会出错【2-5-6.cpp】

int positive(const int n){ 
   static_assert(n>0,"value must > 0");
}

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第3张图片

2-12. 被noexcpt修饰的函数一旦抛异常,编译器就会直接调用std::terminate()来终止程序的运行【2-6-1.cpp】

#include 
using namespace std;
void Throw(){throw 1;} 
void NoBlockThrow(){Throw();}
void BlockThrow() noexcept{Throw();}
int main(){
    try{
        Throw();
    }   
    catch(...){
     cout<<"found throw."<

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第4张图片

2-13.C++11中类的构造和析构函数默认不抛异常,除非显示指定或类的基类或成员有noexcept(false)的析构函数

struct A{
    ~A(){throw 1;} 
};
struct B{
    ~B() noexcept(false) {throw 2;} 
};
struct C{
    B b;
};
int funA(){A a;};
int funB(){B b;} 
int funC(){C c;} 
int main()
{   
    try{
       funB();
    }
    catch(...){
     cout<<"caught funB."<

2-14.C++98的静态成员常量的“就地”声明中的限制

class Init{
public:
   Init():a(0){}
   Init(int d):a(d){}
private:
   int a;
   const static int b = 0;
   int c = 1; // 成员,无法通过编译
   static int d = 0; // 非整形或枚举的常量静态成员,无法通过编译
   static const double e = 1.3;// 非整形或枚举的常量静态成员,无法通过编译
   static const char * const f = "e";// 非整形或枚举的常量静态成员,无法通过编译
};
// 编译选项:g++ -c 2-7-1.cpp
// page-136

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第5张图片

2-15.C++11中使用就地圆括号的表达式列表初始化非静态成员会导致编译出错【2-7-2.cpp】

#include 
using namespace std;
struct C{
   C(int i):c(i){};
   int c;
};
struct init{
   int a = 1;
   string b("hello"); // 无法通过编译
   C c(1);            // 无法通过编译
};
// 编译选项:g++ -std=c++11 -c 2-7-2.cpp
// page-138

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第6张图片

2-16 .C++11中用各种手段初始化非静态成员【2-7-3.cpp】

#include 
using namespace std;
struct Mem{
        Mem(){cout << "Mem default , num "<< num << endl;}
        Mem(int i):num(i){cout << "Mem default , num "<< num << endl;}
        int num = 2;
};
class Group{
        public:
                Group(){cout << "Group default val :"<< val<

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第7张图片

2-17. 多个构造函数且有多个成员变量的时候可以看出C++11新方式带来的便利

#include 
using namespace std;
class Mem{
        public:
                Mem(int i):m(i){}
        private:
                int m;
};
class Group{
        public:
                Group(){}              // 这里就不需要初始化data,mem,data成员了
                Group(int a):data(a){} // 这里就不需要初始化mem,name成员了
                Group(Mem m):mem(m){} //  这里就不需要初始化data,name成员了
                Group(int a,Mem m,string n):data(a),mem(m),name(n){}
        private:
                int data = 1;
                Mem mem{0};
                string name{"Group"};
};
// 编译选项: g++ 2-7-4.cpp -std=c++11 -c
// page-141

2-18.书中在C++98标准中的sizeof不能对非静态成员使用,但为啥我用的C++98标准的时候的编译器是能通过的呢?【2-8-1.cpp】

#include 
using namespace std;
struct People{
  public:
     int hand;
     static People * all;
};
int main(){
     People p;
     cout<< sizeof(p.hand)<

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第8张图片

2-19.书中说C++98在什么友元类的时候需要加class关键字,为啥我的98不加也可以呢?

class Poly;
typedef Poly P;
class Lilei{
   friend class Poly;
};
class Jim{
    friend Poly;
};
class HanMeiMei{
    friend P;
};
int main()
{
  return 0;
}

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第9张图片

2-20.利用2-19中的变化为类模板声明友元【2-9-2.cpp】

class P;
template  class People{
   friend T;
};
People

PP; People Pi; /* g++ -std=c++11 2-9-2.cpp -c page 149 */

2-21.利用危险方法且不优雅的方法写出测试程序【2-9-3.cpp】

#include 
using namespace std;
#ifdef UNIT_TEST
#define private public
class Defender{
#endif
public:
   void Defence(int x,int y){}
   void Tackle(int x,int y){}
private:
   int pos_x = 15;
   int pos_y = 0;
   int speed = 2;
   int stamina = 120;
};
class Attacker{
public:
   void Move(int x,int y){}
   void SpeedUp(float ratio){}
private:
    int pos_x = 0;
    int pos_y = -30;
    int speed = 3;
    int stamina = 100;
};
#ifdef UNIT_TEST
class Validator{
   public:
     void Validate(int x,int y,Defender &d){cout <<"Defender Validate"<

2-22.针对2-21的缺陷,利用类模板与友元结合实现良好的封装代码-【2-9-4.cpp】

#include 
using namespace std;
template  class DefenderT{
public:
   friend T;
   void Defence(int x,int y){}
   void Tackle(int x,int y){}
private:
   int pos_x = 15; 
   int pos_y = 0;
   int speed = 2;
   int stamina = 120;
};

template  class AttackerT{
public:
   friend T;
   void Move(int x,int y){}
   void SpeedUp(float ratio){}
private:
    int pos_x = 0;
    int pos_y = -30;
    int speed = 3;
    int stamina = 100;
};

using Defender = DefenderT; // 普通的类定义,使用int做参数
using Attacker = AttackerT;
#ifdef UNIT_TEST
class Validator{
   public:
     void Validate(int x,int y,Defender &d){cout <<"Defender Validate"<; // 测试专用的定义,Validator类称为友元
using AttackerTest = AttackerT; 

int main()
{
   Defender d;
   Attacker a;
   a.Move(15,30);
   d.Defence(15,30);
   a.SpeedUp(1.5f);
   d.Defence(15,30);
   Validator v;
   v.Validate(7,0,d);
   v.Validate(7,-10,a);
   return 0;
}
#endif 

/*
using Defender = DefenderT;这里用using来定义类型的别名,这个和使用typedef
的定义类型的别名是完全一样的,使用using定义别名是C++11中的一个新特性.
*/

/*
编译选项:g++ 2-9-4.cpp -std=c++11 -DUNIT_TEST
page 153

*/

2-23.基类的成员函数fun被声明为virtual的时候,对于其派生类来说,fun总是可以被重写覆盖的【2-10-1.cpp】

#include 
using namespace std;
class MathObject{
public: 
   virtual double Arith()=0;
   virtual void Print()=0;
};
class Printable:public MathObject{

public:
     double Arith()=0;
     void Print(){
         cout << "Output is:" <<  Arith() << endl;
     }   
};
class Add2:public Printable{
public:
     Add2(double a, double b):x(a),y(b){}
     double Arith(){return x+y;}
private:
     double x,y;
};
class Mul3:public Printable{
public:
     Mul3(double a ,double b,double c):x(a),y(b),z(c){}
     double Arith(){return x*y*z;}
private:
     double x,y,z;
};

// 编译选项:g++ 2-10-1.cpp -c
// page 157

2-24.final关键字使得派生类不可以通过重写来覆盖它所修饰的虚函数

struct Object{
   virtual void fun()=0;
};
struct Base:public Object{
   void fun() final;// 声明为final
};
struct Derived:public Base{
   void fun(); // 无法编译通过
};
// 编译选项:g++ -c -std=c++11 2-10-2.cpp
// page 159

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第10张图片

2-25-1.虚函数描述符override可以帮我们的编译器做一些辅助检查

#include 
using namespace std;
struct Base{
    virtual void Turing() = 0;
    virtual void Dijkstra()=0;
    virtual void VNeumann(int g)=0;
    virtual void DKnuth() const;
    void Print();
};
struct DeriveMid: public Base{
   void VNeumann(double g);// 接口被隔离了,曾想多一个版本的VNeumann,但是其实参数与基类中不一致,编译的时候却没有报错
};
struct DeriveTop: public Base{
        void Turing() override;              // 可以编译通过
        void Dikjstra() override;            // 无法编译通过,拼写错误,并非报的错中写的“没有重写”,应该报成“没有被正确重写”
        void VNeumann(double g) override;    // 无法编译通过,参数不一致,并非报的错中写的“没有重写”,应该报成“没有被正确重写”
        void DKnuth() override;              // 无法编译通过,常量性不一致,并非报的错中写的“没有重写”,应该报成“没有被正确重写”
        void Print() override;               // 无法通过编译,并非报的错中写的“没有重写”,应该是“不能对非虚函数进行重载”
};

/* 虚函数描述符override,如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译
如果不写override,编译器对
(1)函数名拼写错误;
(2)原型函数不匹配:参数类型错误,常量性等特征;
(3)重写非虚函数;
等是不会报错提醒的,有了override可以帮我们的编译器做一些辅助检查.

编译选项:g++ -c -std=c++11 2-10-3-1.cpp
我试了这个:g++ -c  2-10-3-1.cpp 也可以检查出效果
page 162
*/

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第11张图片

2-25-2.一个正确使用override的示例

#include 
using namespace std;
struct Base{
    virtual void Turing() = 0;
};
struct DeriveTop: public Base{
      virtual void Turing() override{cout<<"First derive struct overrides the base's turing function and it must be done as it is defined as pure virtual function in the base class."<

2-26.一个简单的例子回顾一下函数模板【2-11-1-1.cpp】

#include 
using namespace std;
template  void TempFun(T a){ 
  cout << a << endl;
}
int main (){ 

     TempFun(1);// 1,示例化为TempFun(1)
     TempFun("1");// "1",示例化为TempFun("1")
}

/*
编译选项:g++ 2-11-1-1.cpp
page 166
*/

2-27.C++98标准不支持函数模板的默认模板参数,C++11中开始支持函数模板的默认模板参数【2-11-1-2.cpp】

void DefParm(int m = 3){} // C++98 编译通过,C++11编译通过
template // C++98 编译通过,C++11编译通过
    class DefClass{};

template // C++98 编译不通过,C++11编译通过
    void DefTempParm(){};

/*
编译选项:
g++ 2-11-1-2.cpp  -c -std=c++98
g++ 2-11-1-2.cpp  -c -std=c++11

page 167
*/

2-28.C++11中类模板的默认参数必须遵循从右至左的规则,函数模板的默认参数指定没有规则【2-11-2.cpp】

template class DefClass1; // 可以通过编译

template class DefClass2;   // 不可以通过编译

template class DefClass3;          // 可以通过编译

template class DefClass4;          // 不可以通过编译

template  void DefFunc1(T1 a ,T2 b); 
template  void DefFunc2(T a); 

// 编译选项:g++ -c -std=c++11 2-11-2.cpp
// 编译选项:g++ -c -std=c++98 2-11-2.cpp
// page 168

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第12张图片

2-29.模板函数的默认形参不是模板参数推导的依据,函数模板参数的选择,总是由函数的实参推导出来的

template
void f(T t = 0,U i = 0)
void g(){

    f(1,'c');          // f(1,'c')
    f(1);              // f(1,0),使用了默认模板参数double
    f();               // 错误,T无法被推导出来
    f();          // f(0,0),使用了默认模板参数double
    f();     // f(0,0),自定义,可以推导出来
}

// 编译选项:g++ -std=c++11 2-11-3.cpp
// page 170

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第13张图片

2-30.C++11针对C++98中的局部和匿名类型不能作为模板类的实参做出调整【2-13-1.cpp】

【深入理解C++11:C++新特性解析】第2章 保证稳定性和兼容性 测试代码整理_第14张图片

你可能感兴趣的:(C++,C++11)