一、nullptr
nullptr 是用于解决 NULL 和 0 的有疑义关系的。NULL 通常被义为(void*)0。在 如下应用中会引发歧义。
1.1 入参
#include <iostream> using namespace std; void f(int){} void f(bool){} void f(void*){} int main() { f(0); // 调用f(int) f(NULL); // 可能无法编译通过,但一般会调用f(int) f(nullptr); // 调用f(void*) }
1.1.1 解释
- C++ 视 0 首先为 int 型,因此,调用 f(0) 即调用 f(int)
- NULL 的情况复杂些,C++ 首先视其为广义整型。假如 NULL 被定义为普通 的 0,则调用 f(int); 如果 NULL 被定义成 0L,则 long -> int, long -> bool, 0L -> void*, 这三 种情况都是合法的,此时,编译器会报错
- 使用 nullptr,则不会有重载函数调用模糊的问题 - nullptr 不属于广义整型,也不是普通意义上的指针。 - nullptr 的实际类型是 std::nullptr_t,它能够隐式的转换成所有的原始指针 类型,故可将其视为一个可指向所有类型的指针。
1.1.2 办法
避免NULL重载编译报错:
f((int)NULL) 通过强转的办法解决
1.2 返值
使用 0 与 result 作比较,则一时不能确定 findRecord 的返回值类型 (可能是 广义整型,也可能是指针类型); 使用 nullptr,可以清楚地知道 findRecord 的返回值, 必定是一个指针类型。
auto result = findRecord( /* arguments */ ); if (result == 0) { ... } auto result = findRecord( /* arguments */ ); if (result == nullptr) { ... }
二、override
2.1 含义
覆盖重新写从父类继承过来的函数
覆写父类的虚函数时候,好的 IDE 一定会给出斜体等的提示,表示此函数覆写自 父类。
2.2 目的
避免发生,编译通过,但是逻辑错误的情况 例如函数名写错了 但是编译通过,却不是从父类继承过来的函数。
利用关键字 override 则指明,此种覆写关系,若此关系不成立,则以报错的形式提示 给用户。
2.3 问题?
class G{ public: virtual void func(int) { printf("G::func(int) \n"); }; }; class H:G{ public: //virtual void func(int) override{ // printf("H::func(int) \n"); //} virtual void func(double){ printf("H::func(double) \n"); } }; void main() { H *p = new H; p->func(5); p->func(5.0); system("pause"); }
vs2015 输出 H::func(double) H::func(double) 请按任意键继续. . .
疑问???
没调用到G类的func(int)
三、final
3.1 含义
关键字 final 有两个用途:
第一,它阻止了子类继承;
class A final { public: virtual void func() const; }; class B: A //错误 编译报错 A is final { public: void func() const override final;// 错误 编译报错 A is final { };
第二,阻止一个虚函数的覆 写。
class A { public: virtual void func() const; }; class B { public: void func() const override final;//OK }; class C: B { public: void func() const; //error, B::func is final 编译报错 };
3.2 意义
阻止类的无限扩展。
四、 =default =delete
4.1 default
C++ 的类有四类特殊成员函数,它们分别是:
- 默认构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值运算符。
class A { public: A();//构造函数 创建类的实例时 调用 ~A();//析构函数 销毁类的实例时 调用 A(const A &) =;//拷贝构造函数 类实例之间的拷贝 例如A a;A b = a;//此时调用拷贝构造函数 A operator=(const A &);//拷贝赋值运算符 本质重载运算符=进行初始化 }
关键字default
如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员 函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。
class A { public: A() = default; A(int x ):_x(x) {} private: int _x; };
4.2 delete
为了能够让程序员显式的禁用某个函数,C++11 标准引入了一个新特性: "=delete"函数。程序员只需在函数声明后上“=delete;”,就可将该函数禁用。
class Singleton { public: static Singleton* getInstance() { if(_ins == nullptr) _ins = new Singleton; return _ins; } Singleton(Singleton &) = delete; Singleton& operator=(Singleton &) = delete; private: Singleton(){ } static Singleton* _ins; }; Singleton* Singleton::_ins = nullptr; //类的静态变量在类外初始化 int main() { Singleton *ps = Singleton::getInstance(); Singleton ps(*ps); *ps = Singleton::getInstance(); return 0; }
4.3 Raw String Literals
4.3.1 缘由
C/C++中提供了字符串,字符串的转义序列,给输出带来了很多不变,如果需要 原生义的时候,需要反转义,比较麻烦。
4.3.2 使用
C++提供了 R"()",原生字符串,即字符串中无转义,亦无需再反义。但是注意() 中的)"会导至提前结束。
#include <iostream> using namespace std; string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754"; // 错误,\没转义 string path2= "C:\\Program Files (x86)\\alipay\\aliedit\\5.1.0.3754"; //正确,\转义了 用\\或者/,但麻烦 string path3= R"(C:\Program Files (x86)\alipay\aliedit\5.1.0.3754)"; //正确,使用了R() string path4= R"(C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754)";//正确,使用了R() //string path5= R"(C:\Program "Files" (x86)\\alipay\aliedit\)"5.1.0.3754)";//错误,R()的缺陷 int main(int argc, char *argv[]) { cout << path.c_str() << endl; cout << path2.c_str() << endl; cout << path3.c_str() << endl; cout << path4.c_str() << endl; system("pause"); return 0; } 输出结果: C:Program Files (x86)lipayliedit.1.0.3754 C:\Program Files (x86)\alipay\aliedit\5.1.0.3754 C:\Program Files (x86)\alipay\aliedit\5.1.0.3754 C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754
五、Range-Based for 循环Foreach
一种基于STL容器遍历的一种for循环形式 类似Java的 foreach
5.1 语法形式:
for (declaration : expression) statement 例如 vector<int> vecInt = {0,1,2,3,4,5}; //expression 列表 for(auto &i:vecInt){//auto &i is declaration 申明 cout << i << endl; } 等价写法1 vector<int> vecInt = { 1,2,3,4,5 }; for (int i = 0; i<vecInt.size(); i++) { int tmp = vecInt.at(i); cout << tmp << endl; } 等价写法2 vector<int> vecInt = { 1,2,3,4,5 }; vector<int>::iterator itr = vecInt.begin(); for (; itr != vecInt.end(); itr++) { cout << *itr << endl; }
5.2 解释:
expression 部分是一个对象,必须是一个序列,比方说
用花括号括起来的初始值
1.列表
2.数组
int[] a={1,2,3,4,5}; char ch[] = “123”;string str = “china”;
3.vector 或 string 等类型的对象。
vector vet = {1,2,3};list list= {1,2,3} map<int,string>map = {1,“123”};
这些类型的共同特点是拥有能返回迭 代器的 begin 和 end 成员。
declaration 部分负责定义一个变量,该变量将被用于访问序列中的基础元素。
每 次迭代,declaration 部分的变量会被初始化为 expression 部分的下一个元素值。
确 保类型相容最简单的办法是使用 auto 类型说明符。
5.3 关于vector
1.vector::size 会遍历vector 效率会低
六、std::for_each()
#include "stdafx.h" #include<algorithm> #include<iostream> #include<vector> void func(int n) { std::cout << n << std::endl; } int main() { std::vector<int> arr; arr.push_back(1); arr.push_back(2); std::for_each(arr.begin(), arr.end(), func); return 0; }
七、STL容器的使用
7.1 normal init 常规初始化
所谓的常规初始化,即调用类的构 造器。
7.1.1 vector
vector<int> vi; vector<int> vi(10,10);//size 10,each value 10 vector<int> vi(arr,arr+10);//begin,end
7.1.2 list
list<int> li(10); list<int> li2(10); list<int> li3(10,1);//size 10,each value 1 int arr2[10] = {}; list<int> li4(arr2,arr2+10);//begin,end
7.1.3 map
7.1.3.1 查找
7.1.3.2 插入
//map[key] = value map<int,string> mis; mis[0] = "first"; mis[1] = "second"; mis[2] = "third"; //make_pair map<int,string> mis2(mis.begin(),mis.end()); mis.insert(std::make_pair(3, "fourth")); mis.insert(pair<int, string>(3, "fourth")); //value_type mis.insert(map<int, string>::value_type(3, "fourth")); for (auto& pair : mis) cout << pair.first << ":" << pair.second.c_str() << endl;
7.1.3.3 删除
7.2 initialization List {} 列表方式初始化
以初始化列表的方式来进行实始化,打破了,原有的初始化格局,令实始化更直观 化,人性化。
vector<int> vi = {1,2,3,4,5}; list<int> li = {1,2,3,4,5}; map<int,string> mis = { {1,"c"}, {2,"c++"}, {3,"java"},{4,"scala"}, {5,"python"} }; mis.insert({6,"ruby"}); for(auto &is: mis) { cout<<is.first<<is.second<<endl; }
7.3 容器的使用
7.3.1 std::map
插入
查找
删除
7.3.2 std::vector
插入
查找
删除
7.3.3 std::list
插入
查找
删除
八、initializer_list 类
8.1 原理
是使用 {} 而不是 () 调用构造函数,
template< class T > class initializer_list; C++11 中提供了新的模板类型 initializer_list。 initializer_list 对象只能用大括号{}初始化,其内有元素都是 const 的。常用于构造 器和普通函数参数。
8.2 常见用法:
8.2.1 普通函数参数
double sum(const initializer_list<double> &il) { double s = 0; for(auto d:il) s += d; return s; } double s = sum({1,2,3,4,5});
8.2.2 构造器参数
template <typename T> class myarr { private: vector<T> _arr; public: myarr() { cout<<"called myarr()"<<endl; } myarr(const initializer_list<T>& il) { cout<<"called myarr(const initializer_list<T>& il)" << endl; for (auto x : il) _arr.push_back(x); } }; int main() { myarr<int> ma; myarr<int> ma2 = {1,2,3,4,5}; return 0; }
8.3 格式
={},({}),{};
会调用initilizer_list构造
int arr[] = {1,2,3}; int arr2[] {1,2,3}; vector<int> vi = {1,2,3}; vector<int> vi2{1,2,3};