uniform initialization一致性初始化
//赋初值 vector<int>v1{ 1, 12, 6, 0 }; vector<string>v2{ "liming", "jenny", "denny" };
编译器看到以上{}赋值,就会做出一个initializer_list<T>,它的背后是一个array<T,n>,所有容器都有一个参数为initializer_list<T>的构造函数,当用户使用{}来初始化时,编译器会调用那个构造函数。
vector的构造函数
vector(std::initializer_list<value_type> ilist)
{
range_init(ilist.begin(), ilist.end());
}
range_init函数里面会先申请一段连续的空间,把begin和end里的数据一个一个的拷贝到新空间里。
class Person { public: Person(int a, int b) :m_A(a), m_B(b){} void showPerson() const { cout << m_A << " " << m_B << endl; } private: int m_A; int m_B; }; void test01() { Person p{ 1, 2,}; p.showPerson(); }
person的构造函数没有设置为带有initializer_list<T>的参数,这两个元素也会被分配给它的构造函数。
initialzer lists
void print1(std::initializer_list<int>val) { for (auto p = val.begin(); p != val.end(); p++) cout << *p << endl; } print1({ 1, 2, 3, 4, 5, 6 });
explicit
在2.0之前的用法:
用来禁止使用隐式转化法,explicit只能修饰单一实参。
未使用explicit来修饰构造函数的demo
struct person{ int m_a; int m_b; person(int a, int b=0) :m_a(a), m_b(b){ cout << "person的有参构造" << endl; } person operator+(const person&p) { //return person((m_a + p.m_a), (m_b + p.m_b)); this->m_a =m_a+ p.m_a; this->m_b = m_b+p.m_b; return *this; } }; void test01() { person p(4,9); person p1 = p + 5; //5传递给person &p时会调用构造函数 cout << "p1的m_a=" << p1.m_a << " " << "m_b=" << p1.m_b << endl; } //打印结果 //person的有参构造 //person的有参构造 //p1的m_a=9 m_b=9
如果用explicit修饰构造函数,是告诉编译器不要自作聪明帮我做隐式转换。
explicit person(int a, int b=0) :m_a(a), m_b(b){ cout << "person的有参构造" << endl; }
这时候再调用operator+会报错。 二进制“+”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)
在2.0之后,explicit可以修饰多实参构造函数。
for的特殊用法
for (auto i : { 1, 2, 3, 4, 5 }) cout << i << " "; //1 2 3 4 5 cout << endl; vector<int>v{ 1, 2, 3, 4, 5 }; for (auto i : v) cout << i << " "; //1 2 3 4 5 cout << endl;
源码
for(auto _pos=coll.begin(),auto _end=coll.end();_pos!=_end;_pos++) { decl=*_pos ; ... }
for的特殊用法与explicit
class p { public: p(string str) { m_name = str; } string m_name; }; ostream &operator<<(ostream &cout,const p&pp) { cout << pp.m_name; return cout; } vector<string> myv{ "liming", "jenny", "denny" }; for (const p& elem : myv) //这里会发生赋值,会调用构造函数 cout << elem << " "; cout << endl;
如果我们不希望for loop发生类型转换,就在构造函数前面加explicit,这时在赋值操作时就不会发生类型转换。
using
type alias
using func=void(*)(int,int); //func是一个函数指针,指向返回值为void,参数为int,int void foo(int a,int b){...} func=foo;
//标准库某容器 template<class T> class c { using value_type=T; //等价于 typedef T value_type; }
noexcept
void foo() noexcept { ... }
告诉编译器,本函数不会抛出异常。尤其是在设计一个class时,如果他要用vector这中容器,在设计类的move constructor和 move operator=时要告诉编译器我两个函数不会抛出异常,如果不声明为noexcept,编译器可能不敢用这两个函数,那么vector在成长过程要要做大量的拷贝构造。
class person { public: person() { cout << "person的默认构造" << endl; } person(int age) { m_age = new int(age); cout << "person的有参构造" << endl; } //搬离构造 person(person&&p) _NOEXCEPT { cout << "move" << endl; m_age = p.m_age; p.m_age = nullptr; } //搬离赋值 person &operator=(person&&p) _NOEXCEPT { cout << "move=" << endl; if (this != &p) { this->m_age = p.m_age; p.m_age = nullptr; } return *this; } ... };
override
在子类重写父类虚函数时,明确告诉编译器我是在重写,如果不小心写错了参数,编译器会帮助我纠错。
class Base { virtual void func(); } class Son:public Base { void func(int) override{} //编译器会报错 }
final
两种用法:一种用来修饰类,被fianl修饰的class不能被继承;一种用来修饰虚函数,此虚函数不能被子类重写。
右值引用
Rvalue Reference帮助解决不必要的拷贝。当拷贝的来源端是右值,那么接收方就可以去“偷”来源端的数据,接收方就不必去申请新的空间来存放这些数据。
左值一般就是变量,放在左边接受数据,与之相对应就是右值,比如临时对象,右值只能放在右边。
C++新特性认为,当右值出现在operator=的右边,这时对右值进行move,而非拷贝是可行的。这时需要用户在调用端告诉编译器等号右边是个右值,并且在被调用端写出处理这个右值的move assignment。
举个例子,我们自定义了一个类,类中有属性开辟到堆区。
class Person{ public: Person() { cout << "person的默认构造函数" << endl; } Person(int age) { m_Age = new int(age); cout << "person的有参构造" << endl; } Person(const Person&p) { m_Age = new int(*p.m_Age); cout << "person的拷贝构造" << endl; } ~Person() { if (m_Age != NULL) { cout << "person的析构函数" << endl; free(m_Age); m_Age = NULL; } } int *m_Age; };
Person p1(22); Person p2(13); Person p3(34); Person p4(23); vector<Person>v1; v1.emplace(v1.begin(), p1); //在容器起始位置插入一个元素,这个p1并不会当成右值
打印结果
发现他调用了拷贝构造函数。并没有去“偷”要放进来的p1。
C++2.0新特性可以允许我们这样写
class Person{ public: Person() { cout << "person的默认构造函数" << endl; } Person(int age) { m_Age = new int(age); cout << "person的有参构造" << endl; } Person(const Person&p) { m_Age = new int(*p.m_Age); cout << "person的拷贝构造" << endl; } Person(Person &&p)//c++2.0会调用这个构造函数 { m_Age = p.m_Age; p.m_Age = NULL; cout << "person的move构造函数" << endl; } ~Person() { if (m_Age != NULL) { cout << "person的析构函数" << endl; free(m_Age); m_Age = NULL; } } int *m_Age; };
Person p1(22); Person p2(13); Person p3(34); Person p4(23); vector<Person>v1; v1.emplace(v1.begin(), move(p1)); //把p1当成右值
从打印结果来看,并没有调用拷贝构造,而是调用move构造函数,这是个浅拷贝,把要插入的值当作是右值,把它的地址原封不动的赋值过来,为了避免浅拷贝的问题,把右值里的指针清空。成功把右值“偷”过来了。
源码分析
variadic templates
模板参数和个数不定
void print() { } template<typename T,typename ...Types> void print(const T&firstArg,const Types&...args) { cout << firstArg << " "; print(args...); } void test01() { print(1.1, "liming", 100); //打印 不同类型的数据 }
type_traits
在标准库中最主要的用法是判断一个类型他的拷贝构造、拷贝赋值、移动构造、移动赋值、析构函数重不重要。
测试:
//没有写拷贝构造、拷贝赋值、移动构造、移动赋值 class Person { public: Person() = default; Person(int age) { m_age=new int(age); } ~Person() { if (m_age != NULL) { delete m_age; m_age=NULL; } } int *m_age; }; cout << is_trivially_copy_constructible<Person>::value << endl;//判断person类的拷贝构造重不重要,1表示不重要,0表示重要 cout << is_trivially_copy_assignable<Person>::value << endl; cout<<is_trivially_move_assignable<Person>::value << endl; cout << is_trivially_move_constructible<Person>::value << endl; cout << is_trivially_destructible<Person>::value << endl;
打印结果:1 1 1 1 0
class Person { public: Person() = default; Person(int age) { m_age=new int(age); } Person(const Person&p) //拷贝构造 { m_age = new int(*p.m_age); } Person(Person &&p) //移动构造 { m_age = p.m_age; p.m_age = NULL; } Person &operator=(const Person&p) //拷贝赋值 { if (m_age != NULL) { delete m_age; m_age = NULL; } m_age = new int(*p.m_age); return *this; } Person &operator=(Person&&p)//移动赋值 { if (this != &p) { if (m_age != NULL) { delete m_age; m_age = NULL; } m_age = p.m_age; p.m_age = NULL; return *this; } } ~Person() { if (m_age != NULL) { delete m_age; m_age=NULL; } } int *m_age; };
打印结果:0 0 0 0 0 ,表示全都重要。
tuple
tuple是容纳元素集合的对象,这些元素可以是任意个数也可以是任意类型。
std::tuple<int, float, double>t(1, 1.111, 2.0001); std::cout << "t:" << std::get<0>(t) << " " << std::get<1>(t) << " " << std::get<2>(t) << std::endl; auto t2= std::make_tuple(10, 1.1, "liming"); //构建一个变量名为t2的tuple std::cout << "t:" << std::get<0>(t2) << " " << std::get<1>(t2) << " " << std::get<2>(t2) << std::endl; int i; double d; std::string name; std::tie(i, d, name) = t2; //将t2中的数据分别绑定到括号中的变量里 std::cout << "i=" << i << " " << "d=" << d << " name= " << name << std::endl; typedef std::tuple<int, double, std::string> TupleType; std::cout << "tuple_size=" << std::tuple_size<TupleType>::value << std::endl;
tuple源码
template<typename... Values> class tuple; template<>class tuple<>{}; template<typename Head, typename... Tail> class tuple<Head, Tail...>:private tuple<Tail...> { typedef tuple<Tail...> inherited; public: tuple(){} tuple(Head v, Tail... vtail) { m_head = v; inherited(vtail...); } Head head(){ return m_head; } inherited &tail() { return *this; } protected: Head m_head; };