//普通;类型
int a = 1, b = 3;
auto c = a + b;// c为int型
//const类型
const int i = 5;
auto j = i; // 变量i是顶层const, 会被忽略, 所以j的类型是int
auto k = &i; // 变量i是一个常量, 对常量取地址是一种底层const, 所以b的类型是const
int*
const auto l = i; //如果希望推断出的类型是顶层const的, 那么就需要在auto前面加上cosnt
//引用和指针类型
int x = 2;
int& y = x;
auto z = y; //z是int型不是int& 型
auto& p1 = y; //p1是int&型
auto p2 = &x; //p2是指针类型int*
int func() {return 0};
//普通类型
decltype(func()) sum = 5; // sum的类型是函数func()的返回值的类型int, 但是这时不会实际调用函数func()
int a = 0;
decltype(a) b = 4; // a的类型是int, 所以b的类型也是int
//不论是顶层const还是底层const, decltype都会保留
const int c = 3;
decltype(c) d = c; // d的类型和c是一样的, 都是顶层const
int e = 4;
const int* f = &e; // f是底层const
decltype(f) g = f; // g也是底层const
//引用与指针类型
//1. 如果表达式是引用类型, 那么decltype的类型也是引用
const int i = 3, &j = i;
decltype(j) k = 5; // k的类型是 const int&
//2. 如果表达式是引用类型, 但是想要得到这个引用所指向的类型, 需要修改表达式:
int i = 3, &r = i;
decltype(r + 0) t = 5; // 此时是int类型
//3. 对指针的解引用操作返回的是引用类型
int i = 3, j = 6, *p = &i;
decltype(*p) c = j; // c是int&类型, c和j绑定在一起
//4. 如果一个表达式的类型不是引用, 但是我们需要推断出引用, 那么可以加上一对括号, 就变成了引用类型了
int i = 3;
decltype((i)) j = i; // 此时j的类型是int&类型, j和i绑定在了一起
int e = 4;
const int* f = &e;
decltype(auto) j = f;//j的类型是const int* 并且指向的是e





/*方式一、类型转换*/
#include
using namespace std;
void main()
{
short int a = 0x1234;
char c = char(a);//借助int型转换成char型,高地址截去,保留低地址部分
if (c == 0x12)
cout << "big endian" << endl;
else if (c == 0x34)
cout << "little endian" << endl;
}
/*方式二、union联合体*/
#include
using namespace std;
union endian {
short int a;
char c;
};
void main()
{
endian value;
value.a = 0x1234;
if (value.c == 0x12)
cout << "big endian" << endl;
else if (value.c == 0x34)
cout << "little endian" << endl;
}
(1) volatile
比如要往某一地址送两指令:
int *ip =…; //设备地址
*ip = 1; //第一个指令
*ip = 2; //第二个指令
以上程序compiler可能做优化而成:
int *ip = …;
*ip = 2;
结果第一个指令丢失。如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意:
volatile int *ip = …;
*ip = 1;
*ip = 2;
即使你要compiler做优化,它也不会把两次付值语句间化为一。它只能做其它的优化。这对device driver程序员很有用。
如 volatile char a;
a=0;
while(!a){
//do some things;
}
doother();
如果没有 volatile doother()不会被执行
(2) mutable
(3) explicit
**explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换,**注意以下几点:
class A
{
public:
A(){}
A(const A& a){cout << "copy constructor is called" << endl;}
~A(){}
};
void useClassA(A a) {}
A getClassA(){//此时会发生拷贝构造函数的调用,虽然发生NRV优化,但是依然调用拷贝构造函数
A a;
return a;
}
//A& getClassA2()// VS2019下,此时编辑器会进行(Named return Value优化)NRV优化,不调用拷贝构造函数 ,如果是引用传递的方式返回当前函数体内生成的对象时,并不发生拷贝构造函数的调用
//{
// A a;
// return a;
//}
int main()
{
A a1,a2,a3,a4;
A a2 =a1; //调用拷贝构造函数,对应情况1
useClassA(a1);//调用拷贝构造函数,对应情况2
a3 = getClassA();//发生NRV优化,但是值返回,依然会有拷贝构造函数的调用 情况3
a4 = getClassA2(a1);//发生NRV优化,且引用返回自身,不会调用
return 0;
}
在c++中,new有三种类型典型的使用方法:plain new,nothrow new和placement new
(1) plain new
言下之意就是普通的new,就是我们常用的new,在c++中定义如下:
void* operator new(std::size_t) throw(std::bad_alloc);
void* operator delete(void*) throw();
因此plain new在空间分配失败的情况下,抛出异常常std::bad_alloc而不是返回NULL,因此通过判断返回值是否为NULL是徒劳的,举个例子:
#include
#include
using namespace std;
int main()
{
try{
char* p=new char[10e11];//申请字符数组
delete p;
}
catch(const std::bad_alloc& ex){
cout<<ex.what()<<endl;
}
return 0;
}
//执行结果:bad allocation
(2) nothrow new
nothrow new在空间分配失败的情况下是不抛出异常,而是返回NULL,定义如下:
void* operator new (std::size_t,const std::nothrow_t&)throw();
void operator delete(void*) throw();
举个例子:
#include
#include
using namespace std;
int main()
{
char* p=new(nothrow) char[10e11];
if(p==NULL)
cout<<"alloc failed"<<endl;
delete p;
return 0;
}
//运行结果:alloc failed
(3) placement new
这种new允许在一块已经分配成功的内存上重新构造对象或对象数组。placement new不用担心内存分配失败,因为它根本不分配内存,它做的唯一一件事情就是调用对象的构造函数。
void* operator new(size_t,void*);
void operator delete(void*,void*);
#include
#include
using namespace std;
class ADT{
int i;int j;
public:
ADT(){
i=10;j=100;
cout << "ADT construct i=" << i << ",j="<<j <<endl;}
~ADT(){cout << "ADT destruct" << endl;}
};
int main()
{
char* p=new(nothrow) char[sizeof(ADT)+1];
if (p == NULL) cout << "alloc failed" << endl;
ADT *q = new(p) ADT;//placement new:不必担心失败,只要p所指对象的的空间足够ADT创建即可
//delete q;//错误!不能在此处调用delete q;
q->ADT::~ADT();//显示调用析构函数(释放前的准备工作)
delete[] p;
return 0;
}
//输出结果:
//ADT construct i=10,j=100
//ADT destruct
#ifdef _cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
#include
using namespace std;
void fun(char* p){
cout<<"char*"<<endl;
}
void fun(int p) {
cout << "int" << endl;}
int main()
{
fun(NULL);return 0;
}
//输出结果:int,可知c++
const class nullptr_t{
public:
template<class T> inline operator T*() const{return 0;}
template<class C,class T> inline operator T C::*() const {return 0;}
private:
void operator&() const;
} nullptr={};
#include
using namespace std;
void fun(char* p) {
cout<< "char* p" <<endl; }
void fun(int* p) {
cout<< "int* p" <<endl; }
void fun(int p) {
cout<< "int p" <<endl; }
int main()
{
fun((char*)nullptr);//语句1
fun(nullptr);//语句2
fun(NULL);//语句3
return 0;
}
//运行结果:
//语句1:char* p
//语句2:报错,有多个匹配
//3:int p

#include
using namespace std;
int main()
{
double m=1,n=0;
try{
cout<<"before dividing."<<endl;
if(n==0) thorw-1;//抛出int型异常
else if(m==0)
throw-1.0;//抛出double型异常
else
cout << m / n << endl;
cout<<"after diving"<<endl;
}
catch(double d)
cout << "catch (double)" << d << endl;
catch(...)
cout << "catch (...)" << endl;
cout << "finished" << endl;
return 0;
}
//运行结果
//before dividing.
//catch (...)
//finished
int fun() throw(int,double,A,B,C){...};
#include
#include
using namespace std;
class A{
public:
virtual ~A();
};
using namespace std;
int main()
{
A* a=NULL;
try{
cout << typeid(*a).name() << endl; // Error condition
}
catch(bad_typeid){
cout << "Object is NULL" << endl;
}
return 0;
}
//运行结果:bject is NULL