目录
1.列表初始化
int arr1[] = { 1,2,3,4,5 };
int arr2[5] = { 0 };
vector<int> v1{ 1,2,3 };
vector<int> v2 = {1,2,3 };
list<int> l1{ 1,2 };//=
map<int, int> m1 { {1,1},{2,2} };//=
C++98单参数的构造函数支持隐式类型的转换,c++11多参数也支持。如果不想它支持,可以在构造函数前加关键字explicit。
C++11中的vector新增了一个构造函数,参数是initializer_list,这是一个模板,可变参数列表,list也是这样支持的。
Vector(initializer_list<T> l)
:_capacity(l.size())
,_size(0)
{
_array = new T[_capacity];
for(auto e : l)
_array[_size++] = e;
}
template<class K, class V>
class map
{
map(initializer_list<pair<K,V>>& l)
{}
};
2.变量类型推导
在定义变量时,必须给出变量的实际类型。根据对象推演出类型
cout<<typeid(p).name()<<endl;//打印出p的类型
decltype(p) p1;//定义出一个p类型的p1,反向推演。
3.范围for
遍历容器,由迭代器支持,编译器遇到范围for,本质上就把它转化成了迭代器的代码。
4.final与override
final:①放在类的后面,这个类不能被继承,②修饰虚函数,这个虚函数不能被重写
override:修饰子类的虚函数,强制检查子类的虚函数有没有完成重写,如果没有就会报错。帮助我们去检测有没有重写。
5.委派构造函数
5.1构造函数冗余造成重复
一个类有多个构造函数,为了减少程序员书写构造函数的时间。通过委派其他构造函数,多构造函数的类编写更加容易。
5.2委派构造函数
在构造函数的初始化列表阶段调用共用的构造函数,而自己特别的需要做的事在函数里面完成。
6.默认函数控制
6.1显示缺省函数
我写了默认函数,编译器就不会生成默认构造函数,可是我又需要这个默认构造函数,此时可以使用
类名() = default;
强制编译器生成默认构造函数
6.2删除默认函数
禁止编译器生成默认的拷贝构造函数以及赋值运算符重载,防拷贝和赋值,浅拷贝和单例模式
A(const A&) = delete;
A& operator(const A&) = delete
7.右值引用
// a是左值, 10是右值
int a = 10;
int b = a;
int& lr1 = a;//左值引用
int && rr2 = 10;//右值引用
//左值引用能不能引用右值?答案是不能的
//int& lr2 = 10;
//但是可以用const,const引用既能引用左值又能引用右值
const int& lr2 = 10;
//右值引用能否引用左值?答案也是否定的
//但是可以引用move
int&& rr2 = (move)a;
赋值符号左边的值是左值,右边的值不一定是右值。左值是可以被改变的值。
左值引用 的作用:
- 提高效率,减少拷贝(深)
- 里面改变,外面能看到
常量是右值,表达式的返回值是右值,const修饰的不是右值。
C++11中的右值:
- 纯右值:常量,运算表达式
- 将亡值:声明周期将要结束的值,例如函数返回时的临时对象(拷贝的那个)
引用将亡值有意义,移动构造,移动赋值,没有去做真的拷贝,高效。
class String
{
public:
String(const char* str = "")
:_str(new char[strlen(str)+1])
{
strcpy(_str, str);
}
// s1(s2)
// 拷贝构造
String(const String& s)
:_str(new char[strlen(s._str) + 1])
{
cout << "String(const String& s)" << endl;
strcpy(_str, s._str);
}
String& operator=(const String& s)
{
cout << "String& operator=(const String& s)" << endl;
if (this != &s)
{
delete[] _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
}
// 移动构造
String(String&& s)
:_str(s._str)
{
cout << "String(String&& s)" << endl;
s._str = nullptr;
}
// 移动赋值
// s1 = die;
String& operator=(String&& s)
{
cout << "String& operator=(String&& s)" << endl;
swap(_str, s._str);
return *this;
}
~String()
{
delete[] _str;
}
const char* c_str()
{
return _str;
}
private:
char* _str;
};
String operator+(const String& s1, const String& s2)
{
String ret(s1);
//ret += s2;
return ret;
}
String GetString(char* pStr)
{
String ret(pStr);
return ret;
}
#include <queue>
int main()
{
//String s = GetString("hello");
/*String s;
s = GetString("hello");*/
}
右值引用最大的作用就是做参数,STL的库里面的insert(queue,list)是一个左值引用,emplace的参数是一个可变参数的右值引用。
String s("hello");
queue<String> q;
q.push(s);//调用拷贝构造
//调用移动构造
q.push(move(s));
q.emplace(move(s));
q.push("world");//拿的是world的临时对象,是右值(将亡值),如果没有将亡值这种设计,消耗很大
move函数将一个左值变为一个右值,右值被认为是将亡值,别人可以把这个对象抽空。
右值引用减少深拷贝,尽量多去调用移动构造。利用将亡值。尽量push临时对象。
queue<pair<int, int>> prq;
pair<int, int> pr(1, 1);
prq.push(pr);//左值
prq.push(make_pair(2,2));//右值
prq.emplace(3, 3);//emplace是一个可变参数,强制化了必须是右值