- auto
- decltype:有点像auto的反函数,auto可以自动申明一个变量,而decltype可以从一个变量或者表达式中得到其对应的类型。
注意:当float类型变量与double类型变量相加时,返回精度更高的double类型。
注意此处的decltype以及typeid().name()的用法 --------------------------------------------------- float a; double b; decltype(a + b) c; cout << typeid(c).name() << endl; ---------------------------------------------------
#include<iostream> #include<vector> #include<string> #include<typeinfo> #include<cassert> using namespace std; int main() { //decltype实际上有点像auto的反函数,auto可以让你声明一个变量 //而decltype则可以从一个变量或表达式中得到其类型 int i; decltype(i) j = 0; cout << typeid(j).name() << endl; float a; double b; decltype(a + b) c; cout << typeid(c).name() << endl; //由于double精度较高,float精度较低 //所以当double类型的变量与float类型的变量相加时会默认其是精度较高的那一类。 vector<int> vec; typedef decltype(vec.begin()) vectype;//改名为vectype }
- nullptr :
nullptr是为了解决原来C++中NULL的二义性问题而引进的一种新的类型,因为NULL实际上代表的是0。
- 枚举 :
int main() { enum Status{Ok, Error}; enum Status2{Ok, Error};//err, 导致命名冲突, Status已经有成员叫Ok, Error 在c++11新特性中可用下述方法解决 enum class Status {Ok, Error}; enum struct Status2{Ok, Error}; //Status flag2 = Ok; // err,必须使用强类型名称 Status flag3 = Status::Ok; enum class C : char { C1 = 1, C2 = 2}; 指定枚举的底层数据类型 enum class D : unsigned int { D1 = 1, D2 = 2, Dbig = 0xFFFFFFF0U }; cout << sizeof(C::C1) << endl; // 1 cout << sizeof(D::D1) << endl; // 4 cout << sizeof(D::Dbig) << endl; // 4 return 0; }
- 继承构造:
class A { public: A(int i) { cout << "i = " << i << endl; } A(double d, int i) {} A(float f, int i, const char* c) {} // ... }; class B : public A { public: using A::A; //新特性: 继承构造函数 #if 0 B(int x,int y):A(x,y){ } #endif // ... virtual void ExtraInterface(){} };
- 继承的构造函数只能初始化基类中的成员变量,不能初始化派生类的成员变量
- 如果基类的构造函数被声明为私有,或者派生类是从基类中虚继承,那么不能继承构造函数
- 一旦使用继承构造函数,编译器不会再为派生类生成默认构造函数
- 委托构造函数
为了减少程序员书写构造函数的时间。
如果一个类包含多个构造函数,C++ 11允许在一个构造函数中的定义中使用另一个构造函数,但这必须通过初始化列表进行操作,如下:
class Info { public: Info() : Info(1) { } // 委托构造函数 Info(int i) : Info(i, 'a') { } // 既是目标构造函数,也是委托构造函数 Info(char e): Info(1, e) { } private: Info(int i, char e): type(i), name(e) { /* 其它初始化 */ } // 目标构造函数 int type; char name; };
- final:阻止类的进一步派生以及虚函数的进一步重写
class A final{ int a; }; //基类 class B1{ public: virtual void func() final {} }; //派生类重写基类的虚函数 class B2: public B1{ public: virtual void func() {} //无法重写,因为基类中已经使用了final禁止其重写 };
- override:确保在派生类中声明的函数跟基类的虚函数有相同的签名
- default delete
class X { public: X(){} // 手动定义默认构造函数 X(int i) { a = i; } private: int a; }; X obj; //必须手动定义默认构造函数X(){} 才能编译通过 -------------------------------------------------------- class X { public: X()= default; //该函数比用户自己定义的默认构造函数获得更高的代码效率 X(int i) { a = i; } private: int a; }; X obj; ------------------------------------------------------- "=default"函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。 class X { public: int f() = default; // err , 函数 f() 非类 X 的特殊成员函数 X(int, int) = default; // err , 构造函数 X(int, int) 非 X 的特殊成员函数 X(int = 1) = default; // err , 默认构造函数 X(int=1) 含有默认参数 }; 如果使用default指示的办法,那么可以产出比用户定义的无参构造函数有更优的代码 (毕竟是编译器干活) 还有一个作用可以让使用者一眼就看出这是一个合成版本的构造函数 (相当于知道类的作者没干其他事情)
class X { public: ---------------------------------------------------------------- X(); X(const X&) {}; X & operator = (const X &) {return *this;} ---------------------------------------------------------------- X(); X(const X&) {} = delete; // 声明拷贝构造函数为 deleted 函数 X & operator = (const X &) = delete; // 声明拷贝赋值操作符为 deleted 函数 }; int main() { X obj1; X obj2=obj1; // 错误,拷贝构造函数被!!!!!禁用!!!! X obj3; obj3=obj1; // 错误,拷贝赋值操作符被!!!!!!禁用!!!!!! return 0; }
模板实例化:
类模板本身不是类型、对象或任何其他实体。仅包含模板定义的源文件不会生成任何代码。为了出现任何代码,必须实例化模板:必须提供模板参数,以便编译器可以生成实际的类(或函数,来自函数模板)。
类模板必须实例化才能作为一个类来声明和定义类对象,类模板实例化成为模板类,同一个类模板不同的实例之间相互独立。template<class T> struct Z // 模板定义 { void f() {} void g(); // 不会被定义 }; template struct Z<double>; // 显示实例化 Z<double> Z<int> a; // 隐式实例化 Z<int> Z<char>* p; // 无任何实例化生成 p->f(); // 隐式实例化 Z<char> and Z<char>::f(). // Z<char>::g() 不会被声明和定义
C++类模板实例化_jinzhu1911的博客-CSDN博客_类模板的实例化
函数模板作用:
引用:给内存起一个别名,定义时需要初始化。
左值引用 int & func01(){ static int tmp; return tmp; } int main01(){ int a; int &b = a; a的内存现在也叫b int &c = 1; 这种是错的 const int &d = a; const int &e = 1; const int &f = func02(); const int tmp = 10; const int &g = tmp; return 0; } 右值引用 int & func02(){ return 99; } int main02(){ int && a = 10; 10储存的地址块起名叫做a int && b = func02(); int j = 1; int i = 2; int && c = i + j; --------------------- int k = 10; int && d = k; 这种情况是不可以右值引用的,不允许把一个左值赋值给一个右值引用 } 这么看来左值引用是对已经命名的内存块改名 而右值引用是直接对某个数据操作,直接将该数据的存储地址改名。 右值有一个重要的性质:只能绑定到一个将要销毁的对象上。
智能指针: 头文件memory
只需要申请堆区空间,而不需要关心其分配以及释放问题,较为方便。
- unique_ptr
unique_ptr持有对对象的独有权,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。
unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。
离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。
#include<iostream> #include<memory> using namespace std; int main(){ unique_ptr<int> upl(new int(111)); 创建智能指针对象 cout<<"*upl = "<<*upl<<endl; 重载了operator *() unique_ptr<int> up1(new int(11)); up1.reset(); 无参,显式释放堆区内存 up1.reset(new int (22)); 有参,先释放原来堆区内容,再重新给up1绑定一个新的堆区内容 }
- shared_ptr
shared_ptr允许多个该智能指针共享第“拥有”同一堆分配对象的内存,这通过引用计数(reference counting)实现,会记录有多少个shared_ptr共同指向一个对象,一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。
int main() { shared_ptr<int> sp1(new int(22)); shared_ptr<int> sp2 = sp1; cout << "count: " << sp2.use_count() << endl; //打印引用计数 cout << *sp1 << endl; // 22 cout << *sp2 << endl; // 22 sp1.reset(); //显式让引用计数减1 cout << "count: " << sp2.use_count() << endl; //打印引用计数 cout << *sp2 << endl; // 22 return 0; }
- weak_ptr
weak_ptr是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用计数的增加或减少。没有重载 * 和 -> 但可以使用lock获得一个可用的shared_ptr对象
weak_ptr的使用更为复杂一点,它可以指向shared_ptr指针指向的对象内存,却并不拥有该内存,而使用weak_ptr成员lock,则可返回其指向内存的一个share_ptr对象,且在所指对象内存已经无效时,返回指针空值nullptr。
void check(weak_ptr<int> &wp) { shared_ptr<int> sp = wp.lock(); // 转换为shared_ptr<int> if (sp != nullptr) { cout << "still " << *sp << endl; } else { cout << "pointer is invalid" << endl; } } int main() { shared_ptr<int> sp1(new int(22)); shared_ptr<int> sp2 = sp1; weak_ptr<int> wp = sp1; // 指向shared_ptr<int>所指对象 cout << "count: " << wp.use_count() << endl; //打印计数器 cout << *sp1 << endl; // 22 cout << *sp2 << endl; // 22 check(wp); // still 22 sp1.reset(); cout << "count: " << wp.use_count() << endl; cout << *sp2 << endl; // 22 check(wp); // still 22 sp2.reset(); cout << "count: " << wp.use_count() << endl; check(wp); // pointer is invalid return 0; }
摘自:黑马程序员