__func__ 预定义标识符是预置在编译器中的宏,并不包含于任何头文件,所以直接调用即可,基本功能就是返回所在函数的名字
例子:
#include <iostream>
using namespace std;
const char* hello(){return __func__ ;}
int main()
{
cout<<"Standard Clib: "<<__STDC_HOSTED__<<endl;
cout<<"Standard C: "<<__STDC__<<endl;
//cout<<"ISO/IEC"<<_STDC_ISO_10646__<<endl
cout<<hello()<<endl;
}
实际上,按照标准定义,编译器会隐式地在函数的定义之后定义__func__标识符
比如上面的hello()函数等于:
const char* hello()
{
static const char* __func__ = "hello";
return __func__;
}
在C++11中,标准允许__func__使用在类或者结构体中
#include <iostream>
using namespace std;
class Test{
const char* name;
public:
Test():name(__func__){}
const char* get(){return name;}
};
int main()
{
Test t;
cout<<t.get();
}
!!!但是将__func__用于函数参数的默认值是不允许的!这是因为在参数声明时,__func__还未被定义
_pragma 操作符
#pragma 是一条预处理的指令,是用来向编译器传达语言标准外的一些信息
例如:
#pragma once 会指示编译器,该头文件只被编译一次,和以下代码达到同样的效果
_Pragma 是与 #pragma功能相同的操作符
格式如下
__VA_ARGS__ 可以在宏定义的实现部分中替换省略号所代表的字符串
比如:
#define PR(...)printf(__VA_ARGS__)
例子:
#include <iostream>
#define LOG(...){\
fprintf(stderr,"\n%s:Line %d :\t",__FILE__,__LINE__);\
fprintf(stderr,__VA_ARGS__);\
fprintf(stderr,"\n");\
}
int main()
{
int x=3;
LOG("x= %d" ,x);
}
运行结果:
long long 整形有两种 long long 和 unsigned long long
long long 整形至少有64位,在写常数字面量时,可以使用LL(ll)后缀 来标识一个 long long 类型的字面量
而ULL (ull)表示一个unsigned long long 类型的字面量。 例子:
例子:
#include <limits>
#include <iostream>
using namespace std;
int main()
{
long long ll_min = LLONG_MIN;
long long ll_max = LLONG_MAX;
unsigned long long ull_max = ULLONG_MAX;
cout<< "min of long long "<<ll_min<<"\nmax of long long "<<ll_max <<"\nmax of unsigned long long "<<ull_max<<endl;
}
结果:
c++ 一共定义了下列5种标准的有符号整形
同时,每一种有符号整数都有一种对应的无符号整数版本,且有符号整形和其对应的无符号整形具有相同的存储空间大小。
比如 signed int 对应的无符号版本的整形是 unsigned int
简而言之,c++11规定,扩展的整形必须和标准类型一样,有符号类型和无符号类型占用同样大小的内存空间。
当运算,传参等类型不匹配时,整形之间会发生隐式的转换
比如(int) a + (long long)b 通常会导致变量a 被提升为long long 类型后再与b进行运算
转化的规则由等级决定
1 长度越大的整形等级越高,比如long long int 的等级会高于int
2 长度相同的情况下,标准整形的等级高于扩展类型,比如long long int 和 _int64中,long long int 的等级更高
3 相同大小的有符号类型和无符号类型的等级相同 , long long int 和 unsigned long long int 的等级相同
当进行隐式的整形转换时,会按照低等级向高等级转换,有符号的转换为无符号的
比如定义_int128_t 为128位的有符号整数(对应的无符号整数_uint128_t),与任何短于它的类型的数据b进行运算时,都会导致b被隐式地转换为_int128_t的整形
C与C++混合编码
断言就是将一个返回值必须需要为真多判别式放在语句中,用于排除在设计的逻辑上不应该出现的情况。
<assert.h>头文件中提供了assert 宏,用于在运行时进行断言
如果不满足为真,则程序会异常终止
也可以通过定义NDEBUG 来禁止assert宏,assert的实现如下:
一旦定义了NDBUG宏,assert宏就将被展开为一条无意义的C语句
静态断言:
static_assert 接收两个参数,一个是断言表达式,一个则是警告信息
静态断言在编译时期就能完成,所以叫静态断言,static_assert的断言表达式的结果必须是在编译时期就可以计算的表达式
也就是必须是常量表达式
例子:
#include <iostream>
using namespace std;
#define assert_static(e)\
do{\
enum{assert_static__=1/(e)};\
}while(0)
template <typename t,class u>
int bit_copy(t& a,u& b)
{
static_assert(sizeof(b)==sizeof(a) , "the parameters of bit_copy must have same width");
}
int main()
{
int a=0x2468;
double b;
bit_copy(a,b);
}
结果编译时报错:
noexcept 修饰符 和 noexcept 操作符
例如下面的异常声明表达式:
throw (int,double)是一个动态异常声明,指出了excpt_func()可能抛出的异常的类型。
noexcept 表示修饰的函数不会抛出异常。如果noexcept修饰的函数抛出了异常,编译器就可以选择直接调用terminate()函数来终止程序的运行(但这种调用方式也会有很多问题:无法保证对象的析构函数正常调用,无法保证栈的自动释放)
有下列两种声明模式:
常量表达式的结果会被转换成一个bool类型的值,该值为true,表示函数不会抛出异常,反之有可能抛出异常。
而不带常量表达式的noexcept相当于声明了noexcept(true),既不会抛出异常
例子:
#include <iostream>
using namespace std;
void Throw()
{
throw 1;
}
void NoBlockThrow()
{
Throw();
}
void BlockThrow()noexcept
{
Throw();
}
int main()
{
try
{
Throw();
}
catch(int x)
{
cout<<"Found throw"<<x<<endl;
}
try
{
NoBlockThrow();
}
catch(int y)
{
cout<<"throw is not blocked"<<y<<endl;
}
try
{
BlockThrow();
}
catch(int z)
{
cout<<"Found throw 1.."<<endl;
}
}
BlockThrow 本是不应该抛出异常的,但抛出了异常所以终止了程序运行
结果:
noexcept 作为一个操作符时可以用于模版:
fun函数是否是一个noexcept的函数,将由T()表达式是否抛出异常所决定。
第二个noexcept就是一个noexcept操作符,当其参数是一个有可能抛出异常的表达式时,则返回false ,否则返回true。
noexcept 的更大的作用是保证应用程序的安全,比如C++11默认将delete函数设置为noexcept就可以提高应用程序的安全性
同样c++11标准中让类的析构函数默认也是noexcept(true) 的。当然,如果程序员显示地为析构函数指定了noexcept。
例子:
#include <iostream>
using namespace std;
class A{
public:
~A()
{
cout<<"~A()"<<endl;
throw 1;
}
};
class B{
public:
~B()noexcept(false)
{
cout<<"~B()"<<endl;
throw 2;
}
};
class C{
private:
A a;
B b;
public:
~C()
{
cout<<"~C()"<<endl;
}
};
void funcA(){A a;}
void funcB(){B b;}
void funcC(){C c;}
int main()
{
try{
funcB();
}
catch(int x)
{
cout<<x<<endl<<"caught funcB."<<endl;
}
try{
funcC();
}
catch(int y)
{
cout<<y<<endl<<"caught funcC."<<endl;
}
try{
funcA();
}
catch(int z)
{
cout<<z<<endl<<"caught funcC."<<endl;
}
}
快速初始化成员变量:
C++11 中,运行使用初始化列表,等号= ,花括号{}就地对非静态成员变量初始化。
但对非静态成员用()括号进行就地初始化,是不行的。
例子:
#include <iostream>
using namespace std;
struct Mem{
Mem()
{
cout<<"Mem default , num: "<<num<<endl;
}
Mem(int i):num(i)
{
cout<<"Mem,num : "<<num<<endl;
}
int num = 2;
};
class Group{
public:
Group()
{
cout<<"Group default . val: "<<val<<endl;
}
Group(int i):val('G'),a(i)
{
cout<<"Goup 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();
}
运行结果:
sizeof是一个特殊运算符,在C++11中使用sizeof操作是合法的,而在c++98里并不支持。在c++98里没有定义类实例时要获得类成员的大小可以采用如下操作:
强制转换0为一个People类的指针,继而通过指针的解引用获得其成员变量
例子:
#include <iostream>
using namespace std;
class 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;
}
扩展的friend语法
friend 关键字用于声明类的友元,友元可以无视类中成员的属性。无论成员是public,protected,private
友元类或友元函数都可以访问
在C++11中,声明一个类为另外一个类的友元时,不需要再使用class关键字
在C++11中,可以为类模版声明友元
在使用类P为模版参数时,P是People<P>的一个friend类,而在使用内置int类型作为模版参数时,People<int>会被实例化为一个普通的没有友元定义的类型。
例子:
#include <iostream>
class Validator;
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>;
using Attacker = AttackerT<int>;
using DefenderTest = DefenderT<Validator>;
using AttackerTest = AttackerT<Validator>;
class Validator
{
public:
void Validate(int x,int y,DefenderTest& d){}
void Validate(int x,int y,AttackerTest& a){}
};
int main()
{
DefenderTest d;
AttackerTest 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(1,-10,a);
return 0;
}