目录
2-2.利用宏定义__func__打印文件名【2-1-2.cpp】
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-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-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