一、直接初始化与拷贝初始化
在一个类对象的定义过程中,对象初始化分为两种:
1)、直接初始化, 要求编译器使用普通的函数匹配,选择我们提供的最匹配的 构造函数 ,
2)、拷贝初始化, 要求编译器将右侧运算对象拷贝到正在创建的对象中,通常使用 拷贝构造函数 来完成。
//直接初始化,使用string的构造函数string (size_t n, char c);
string dots(10,'s');
//直接初始化,使用拷贝构造函数string (const string& str);
string s(dots);
//使用拷贝赋值运算符,拷贝初始化,使用拷贝构造函数string (const string& str);
string s1 = dots;
//拷贝初始化
string null_book = "9-99-999";
//拷贝初始化
string nines = string(10,'s');
二、拷贝构造函数-定义
在对象拷贝过程中,如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,进行的是浅拷贝!
即使定义了其它构造函数,编译器也会合成一个拷贝构造函数,即合成拷贝构造函数。
class Foo{
public:
Foo(); //默认构造函数
Foo(const Foo&); //拷贝构造函数
}
一个拷贝构造函数的构成:
1、一个构造函数的第一个参数是自身类型的引用
2、且任何额外参数都有默认值
拷贝初始化的使用场景:
1、将一个对象作为实参传递给一个非引用类型的形参
class Foo {
int a;
Foo* next;
public:
Foo() = default;
Foo(int p) :a(p), next(nullptr) {}
Foo(int p, Foo* nex) :a(p), next(nex) {}
Foo(const Foo& p) :a(p.a), next(p.next) {}
~Foo() = default;
void copy1(Foo s);
void copy(Foo& s);
Foo* out();
Foo out2();
};
void Foo::copy1(Foo s) //实参到形参,调用拷贝构造函数
//实参到形参发生的拷贝中,next是一次浅拷贝
//next访问的仍是实参next指向的内存
{
a = s.a;
next = s.next;
}
void Foo::copy(Foo& s) //只是引用,未调用拷贝构造函数
{
a = s.a;
next = s.next;
}
2、从一个返回类型为非引用类型的函数返回一个对象
Foo* Foo::out() //返回的this指针赋值给新指针时,调用拷贝构造函数
{
return this;
}
Foo Foo::out2() //返回值会被赋值给一个对象,使用拷贝构造函数
{
Foo ret(a);
ret.next = new Foo(2);
return ret;
}
3、用花括号列表初始化一个数组中的元素或一个聚合类中的成员
Foo nin(9, six); //使用构造函数,构造新对象
Foo ten = {10,six}; //使用列表初始化格式对应的构造函数创建临时对象
//再调用拷贝构造函数
三、深拷贝与浅拷贝
在定义拷贝构造函数时,会发生不同性质的拷贝过程,主要分为深拷贝和浅拷贝。
Foo(const Foo& p) :a(p.a), next(p.next) {} //浅拷贝
Foo(const Foo& p) //深拷贝
{
a = p.a;
next = new Foo();
next ->a = p.next->a;
next ->next = p.next ->next;
}
其特点为:
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间。
深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
移动拷贝构造函数
可以支持移动操作,需要定义移动构造函数和移动赋值运算符。
会将给定对象的资源“窃取”,而不是拷贝资源。
一旦完成资源移动,源对象必须不在指向被移动的资源。
Foo(const Foo&& p) :a(p.a), next(p.next) //移动拷贝
{
p.next = nullptr; //这样释放p是安全的
}
该类类型的右值引用。
左值与右值的区别
左值持久,右值短暂
1、所引用的对象将要被销毁,字面值常量或者临时变量
2、该对象没有其它用户
左值是变量,右值不能被赋值
左值引用与右值引用
右值引用是为了支持移动操作,通过&&来获得右值引用。
右值引用可以自由接管所引用对象的资源。
C++11新特性:std::move(),可以将一个左值转换为对应的右值引用类型。
左值引用是相对于右值引用的,通过&来获得。
左值引用指向源对象的资源。
可以定义左值引用自己的写权限。
比如const引用就不可以修改源对象的值。