【深入理解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 <iostream>
using namespace std;
int main()
{
    cout << "Standard Clib:"<<__STDC_HOSTED__<<endl;
    cout << "Standart C:"<<__STDC__<< endl;
    //cout << "C Statndard version:" << __STDC_VERSION__<<endl;
    cout << "ISO/IEC" << __STDC_ISO_10646__<< endl;
    return 0;
}

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

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

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

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

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

#include <stdio.h>
#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 <iostream>
#include <cstdio>
#if __cplusplus < 201103L
   #error "should use C++11 implementation"
#endif
int main()
{
  printf("test\n");
}

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

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

#include <climits>
#include <cstdio>
#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 <cassert>
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;
}

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

#include <cassert>
#include <cstdio>
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 <cassert>
#include <cstring>
using namespace std;
template<typename T,typename U>
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 <cstring>
using namespace std;
#define assert_static(e)\
do{\
   enum{assert_static__ = 1/(e)};\
} while(0)

template<typename T,typename U> 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;
}

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

#include <cstring>
using namespace std;
template<typename T,typename U> 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");
}

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

#include <iostream>
using namespace std;
void Throw(){throw 1;} 
void NoBlockThrow(){Throw();}
void BlockThrow() noexcept{Throw();}
int main(){
    try{
        Throw();
    }   
    catch(...){
     cout<<"found throw."<<endl;
    }   
    try{
      NoBlockThrow();   
    }   
    catch(...){
     cout<<"throw is not blocked."<<endl;
    }   
    try{
      BlockThrow();//The function decorated by noexcept will call std::terminate() when throw the exception.
    }   
    catch(...){
      cout<<"Found throw 1." << endl;
    }   
}
// g++ -std=c++11 2-6-1.cpp 
// page-128

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."<<endl;// caught funB
    }
    try{
       funC();
    }
    catch(...){
     cout<<"caught funC."<<endl;// caught funC
    }
    try{
       funA();//  terminate called after throw an instance of 'int'
    }
    catch(...){
     cout<<"caught funA."<<endl;// caught funA
    }
}

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

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

#include <string>
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

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

#include <iostream>
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<<endl;}
                Group(int i):val('G'),a(i){cout <<"Group.val:"<< val << endl;}
                void NumOfA(){cout << "Number of A :"<< a.num << endl;}
                void NumOfB(){cout << "Number of B :"<< b.num << endl;}

        private:
                char val{'g'};
                Mem a;
                Mem b{19};
};

int main(){
       Mem member;
       Group group;
       group.NumOfA();
       group.NumOfB();
    
       Group group2(7);
       group2.NumOfA();
       group2.NumOfB();
}
// 编译选项:g++ -std=c++11 -c 2-7-3.cpp
// page-139

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

#include <string>
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 <iostream>
using namespace std;
struct People{
  public:
     int hand;
     static People * all;
};
int main(){
     People p;
     cout<< sizeof(p.hand)<<endl;
     cout<< sizeof(People::all)<<endl;
     cout<< sizeof(People::hand)<<endl;
}


/*
 书中Page144中说到,在C++引入类类型之后,sizeof的定义随之进行了拓展,不过C++98标准中,对
非静态成员变量使用sizeof是不能够通过编译的,但我用c++98标准编译的时候还是能通过的,这个是
为啥呢?难道是做了啥拓展吗?还是标准是标准,我不采用标准也行呢???
*/

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;
}

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

class P;
template <typename T> class People{
   friend T;
};
People<P> PP; 
People<int> Pi; 


/*
g++ -std=c++11 2-9-2.cpp -c
page 149
*/

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

#include <iostream>
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"<<endl;}
     void Validate(int x,int y,Attacker &d){cout <<"Attacker Validate"<<endl;}
};
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
// 编译选项:g++ 2-9-3.cpp -std=c++11 -DUNIT_TEST
// page 151

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

#include <iostream>
using namespace std;
template <typename T> 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 <typename T> 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>; // 普通的类定义,使用int做参数
using Attacker = AttackerT<int>;
#ifdef UNIT_TEST
class Validator{
   public:
     void Validate(int x,int y,Defender &d){cout <<"Defender Validate"<<endl;}
     void Validate(int x,int y,Attacker &d){cout <<"Attacker Validate"<<endl;}
};
using DefenderTest = DefenderT<Validator>; // 测试专用的定义,Validator类称为友元
using AttackerTest = AttackerT<Validator>; 

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<int>;这里用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 <iostream>
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

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

#include <iostream>
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
*/

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

#include <iostream>
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."<<endl;}
};
struct DeriveDeriveTop:public DeriveTop{
     void Turing(){cout <<"Second derive struct overrides the first's turning function."<< endl;}   
};
int main()
{
   DeriveDeriveTop top1;
   top1.Turing();
   return 0;
}
/*
编译选项:g++ -c -std=c++11 2-10-3-1.cpp
我试了这个:g++ -c  2-10-3-1.cpp 也可以检查出效果
*/

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

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

     TempFun(1);// 1,示例化为TempFun<const int>(1)
     TempFun("1");// "1",示例化为TempFun<const char*>("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<typename T= int> // C++98 编译通过,C++11编译通过
    class DefClass{};

template<typename T= int> // 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<typename T1,typename T2 = int> class DefClass1; // 可以通过编译

template<typename T1=int,typename T2> class DefClass2;   // 不可以通过编译

template<typename T,int i = 0> class DefClass3;          // 可以通过编译

template<int i = 0,typename T> class DefClass4;          // 不可以通过编译

template<typename T1=int,typename T2>  void DefFunc1(T1 a ,T2 b); 
template<int i = 0,typename T>  void DefFunc2(T a); 

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

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

template<class T,class U = double>
void f(T t = 0,U i = 0)
void g(){

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

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

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值