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,s2为string:
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知识点