C++primer知识点

Posted 勇二郎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++primer知识点相关的知识,希望对你有一定的参考价值。

 

二十二:

1:拷贝控制操作

拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,析构函数。

这些,在类的数据成员都能默认构造,拷贝,复制,销毁时,编译器默认都会有合成的版本。

(1)   拷贝构造函数:

Foo(const Foo&);

第一个参数是自身类类型的引用,额外的参数都有默认值。

几种情况下会被隐式使用,所以,不能是explicit

    默认拷贝构造函数,又叫合成拷贝构造函数,也会逐元素的拷贝数组的成员。

    拷贝初始化是依靠拷贝构造函数和移动构造函数来完成的。

调用的情况:

    0初始化时,用=或者I()直接初始化时用:对象赋值/拷贝对象时,=非初始化会调用拷贝赋值运算符。

1)函数传递非引用参数的对象

2)函数返回非引用对象

3)花括号列表初始化数组或者聚合类的成员时。

4)标准库容器初始化或调用insert/push成员时。而emplace是进行直接初始化。

其中第1)条解释了,拷贝构造函数的参数为什么是引用了,要拷贝实参,要调用拷贝构造函数,又要拷贝实参,无限循环。

 

    拷贝初始化中,编译器可以跳过拷贝/移动构造函数,直接创建对象,即,允许:

    stringstr = 123; //拷贝构造函数 

改写成:

    stringstr(123);//略过了拷贝构造函数

即使略过了,拷贝/移动构造函数必须是存在且可访问的。

 

 

2)拷贝复制运算符

    名为operator=的函数。必须定义为成员函数。返回指向其左侧运算对象的引用。

3)析构函数

    首先指向函数体,然后销毁成员,成员按初始化顺序逆序销毁。(析构部分是隐式的),隐式销毁内置指针类型成员,并不会delete掉所指向的对象,而智能指针是类类型,所以只能指针在析构阶段会被自动销毁。

 

    如果一个类需要析构函数,几乎可以肯定需要“拷贝构造函数”和“拷贝赋值运算符”。(这三个函数是联系很紧密的,涉及到资源的申请释放),拷贝赋值进行的操作相当于析构和拷贝构造的组合。

 

4

=default ,(1)类内是内联,类外不是内联(2)只能用于编译器可以合成的函数。

=delete (1)必须用于函数第一次声明时。(2)可以用于任何函数。

析构函数或者类成员的析构函数定义为删除的,那么不能定义该类的变量或临时对象,因为对象无法销毁。(虽然可以动态分配这种类型的对象,但是不能释放

 

合成的拷贝控制成员可能是删除的:

虽然我们可以给引用赋值,但是改变的不是引用本身,而是他原来引用的对象。所以:对于有引用成员的类,合成拷贝赋值运算符被定义为删除的。


 

 

(5)move 定义在utility头文件中。使用时要直接调用std::move,防止类定义自己的move,即使我们在调用move前声明usingstd::move,但是实参的命名空间要优先于本作用域内的搜索。(可以将左值转化为右值,返回一个右值)

 

6)移动,会大幅度提高性能。且io类和unique_ptr(包含不能被拷贝的资源:指针或IO缓冲)不能拷贝,但可以移动。

右值引用:必须绑定到右值的引用。性质:只能绑定到将要销毁的对象。

左值引用代表对象身份。右值引用代表对象的值。

int i = 42;

int &r1 = i*32;//错误

const int &r1 = i*32;//正确,可以将const引用绑定到右值上

int && r2 = i*32;//正确,右值引用

 

前置递增/递减 返回左值。

后置递增/递减 返回右值。

      int ia = 10;

      //ia++ = 5;//错误

       ++ia = 6;//正确

 

不能将右值引用绑定到右值引用类型的变量上。

int &&r1 = 42;

int &&r2 = r1;//错误

 

我们可以销毁一个移后原对象,也可以对他赋值,但是不能使用它,即希望他会有什么值。

 

7)移动构造函数

第一个参数是右值引用,其他参数必须有默认实参。

不分配任何内存,但记得要把原对象做好处理:移后原对象可以被安全的销毁或者赋值。

    如果,确认不会抛出异常,可以在参数列表的小括号后加上 noexcept(声明和定义中都要有)告诉编译器,否则编译器要做一些额外的工作。

 


 

 

必须定义拷贝控制成员的类,是有些类成员必须经过拷贝成员的操作。通常移动操作比拷贝要节省效率。

 

移动容器元素可以使用移动迭代器,解引用生成右值引用。

 

拷贝成员通常参数是const T&的,移动成员通常不是const,T&&,逻辑上,一个不改变,一个改变。

 

8

有时右值的使用方式令人惊讶:

s1,s2string:

s1+s2 = wow;//对右值赋值,这里是允许的。。

 

新标准库允许向右值赋值。我们可以阻止这种操作。即强制左侧运算对象(即,this指向的对象)是一个左值。

    我们指出this左值/右值属性的方式与定义const成员函数相同。在参数列表后放置一个引用限定符。(声明与定义都要有),引用限定符必须跟在const之后

Foo &operator=(const Foo&)&;//只能向可修改的左值赋值。

Foo sorted() &&;//可用于可改变的右值

Foo sorted() const &;//可用于任何类型的Foo

 

    定义const成员函数,可以一个有,一个没有。

    但是对于引用限定的函数:如果函数名,参数都相同,必须“都加引用限定符或者都不加”

 

例子1

#include<iostream>

#include<string>

#include<vector>

#include<numeric>

#include<algorithm>

using namespace std;

 

class X

{

public:

    X():data{1,4,3}

    {

    }

    vector<int>data;

    Xsorted() const &

    {

       cout<<"const&"<<endl;

       Xret(*this);                          //1/2

       sort(ret.data.begin(),ret.data.end()); //1

       returnret;                             //1

       //returnret.sorted();                  //2无限递归

       //returnX(*this).sorted();             //3右值sorted

    }

    Xsorted() &&

    {

       cout<<"&&&&&&&&"<<endl;

       sort(data.begin(),data.end());

       return*this;

    }

};

 

int main()

{

    Xx;

    Xx2 = x.sorted();

    for(auto e : x2.data)

    {

       cout<< e << " ";

    }

    cout<< endl;

 

    getchar();

    return0;

}

 

 

例子2

#include<iostream>

#include<string>

#include<vector>

#include<list>

#include<array>

#include<numeric>

#include<map>

#include<unordered_map>

#include<memory>

#include<new>

#include<algorithm>

usingnamespacestd;

classMyClass

{

public:

      MyClass() :str("default")

       {

             cout <<"合成构造函数" <<endl;

       }

      MyClass(stringstr1) :str(str1)

       {

             cout <<"string参数构造函数" <<endl;

       }

      MyClass(constMyClass&you)

       {

             str =you.str;

             cout <<"const拷贝构造" <<endl;

       }

      MyClass(MyClass &you)

       {

             str =you.str;

             cout <<"const拷贝构造"<<endl;

       }

      MyClass& operator=(MyClass &you)

       {

             cout <<"拷贝赋值"<<endl;

             this->str = you.str;

             return *this;

       }

      MyClass(MyClass&&you )

       {

             str =you.str;

             cout <<"const移动构造"<<endl;

       }

      MyClass(constMyClass &&you)

       {

             str =you.str;

             cout <<"const移动构造" <<endl;

       }

      MyClass& operator=(MyClass&&you)

       {

             cout <<"移动赋值"<<endl;

             str =you.str;

             return *this;

       }

 

      voidstrOut()

       {

             cout <<str<<"-----------------------" <<endl<<endl;;

       }

private:

      stringstr;

};

 

intmain()

{

      conststringss ="const str";

      constMyClassm0;//默认构造

      cout <<"const MyClass m0;" <<endl<<endl;

 

      MyClassm1;//默认构造

      cout <<"MyClass m1;" <<endl<<endl;

 

      MyClassm2 ="m2";//string参数构造函数

      cout <<"MyClass m2 =\\"m2\\";" <<endl <<endl;

 

      MyClassm3("m3");//string参数构造

      cout <<"MyClassm3(\\"m3\\");" <<endl <<endl;

 

      MyClassm4(ss);//string参数构造

      cout <<"MyClass m4(ss);" <<endl<<endl;

 

      MyClassm5(std::move(ss));//string参数构造===

      cout <<"MyClass m5(std::move(ss));=====" <<endl<<endl;

 

      MyClassm6 =m2;//拷贝构造const

      cout <<"MyClass m6 = m2;" <<endl<<endl;

 

      MyClassm6_1 =m0;//const拷贝构造

      cout <<"MyClass m6_1 = m1;" <<endl<<endl;

 

      MyClassm7(m2);//拷贝构造const

      cout <<"MyClass m7(m2);" <<endl<<endl;

 

      MyClassm8(std::move(m3));//移动构造const

      cout <<"MyClassm8(std::move(m3));" <<endl <<endl;

 

      m8 =std::move(m3);//移动赋值

      cout <<"m8 = std::move(m3);" <<endl<<endl;

C++primer知识点

C++primer知识点

C++primer知识点

C++primer知识点

C++primer知识点

超硬核知识:两万字总结《C++ Primer》要点!