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;
    };