Copy Constructor¶
Copying¶
Create a new object from an existing one.
- Copying is implemented by the copy constructor
- Has the unique signature
T::T(const T&);
- C++ builds a copy ctor for you if you don't provide one!
如果没有给拷贝构造, C++ 会自动创造一个
memberwise 而非 bitwise. 如果有成员是一个对象,会调用对象自己的拷贝函数。
如果有成员变量是指针,会和原来对象一样指向同一块内存。如果有一个对象被析构,那么这块内存就被 delete, 这就变成了无效内存。
因此我们不一定要有拷贝构造函数,有指针时必须要有。- Copies each member variable
- Good for numbers, objects, arrays
Copies each pointer
- Good for numbers, objects, arrays
- Data may become shared!
- Copies each member variable
什么时候拷贝构造会被调用?
- During call by value
只有对象本身才会有拷贝构造(指针、引用不会) - During initialization
定义变量时做的是初始化,其他时候是赋值。
初始化是要拷贝构造,赋值要重载赋值运算符。 - During function return
不同编译器可能会做不同的优化。函数返回一个对象时,可能会发生拷贝构造。
一个对象只能被构造(初始化)一次,析构一次,但可以被赋值很多次。
string
有自己的拷贝构造函数。
Copy ctor guidelines
- In general, be explicit
Create your own copy ctor -- don't rely on the default - If you don't need one declare a
private
copy ctor
私有的拷贝构造函数使得对象不能被拷贝构造。- prevents creation of a default copy constructor
- generates a compiler error if try to pass-by-value - don't need a defintion
types of function parameters and return value¶
way in
- a new object is to be created in f
void f(Student i);
- better with const if no intend to modify the object
void f(Student *p);
- better with const if no intend to modify the object
void f(Student& i);
way out
- a new object is to be created at returning
Student f();
- what should it points to?
Student* f();
- what should it refers to?
Student& f();
char *foo() {
char *p;
p = new char[10];
strcpy(p, "something");
return p;
}
void bar() {
char *p = foo();
printf("%s", p);
delete p;
}
p
本身是本地变量,但指向的地方是全局的空间。
Note
- Pass in an object if you want to store it
函数要存一个对象,不用用指针或者引用传入,而是直接传入。 - Pass in a const pointer or reference if you want to get the values
- Pass in a pointer or reference if you want to do something to it
- Pass out an object if you create it in the function
如果创建了新的对象,就要传对象出去。 - Pass out pointer or reference of the passed in only
传出去的指针/引用只能是传入的指针/引用。 - Never new something and return the pointer
在哪里new
就在哪里delete
.(类内是可以的,比如在构造的new
在析构的时候delete
)
Move Constructor¶
Left Value vs Right Value¶
- 可以简单地认为能出现在赋值号左边的都是左值:
- 变量本身、引用
*
、[]
运算的结果
- 只能出现在赋值号右边的都是右值
- 字⾯量
- 表达式
- 调用只能接受左值—>引用是左值的别名
- 调用函数时的传参相当于参数变量在调用时的初始化
&&
成为右值引用。
int x=20; // 左值
int&& rx = x * 2; // x*2的结果是一个右值,rx延长其⽣命周期
int y = rx + 2; // 因此你可以重用它:42
rx = 100; // 一旦你初始化一个右值引用变量,该变量就成为了一个左值,可以被赋值
int&& rrx1 = x; // 非法:右值引用无法被左值初始化
const int&& rrx2 = x; // 非法:右值引用无法被左值初始化
x
不存在了,右值引用依然可以使用。注意右值引用本身是个左值。
int x = 10;
int &&a = x + 2;
int y = a + 2;
cout << y << endl;
x = 5;
cout << y << endl;
a = 40;
cout << y << endl;
cout << a << endl;
int &&b = x; // ERR: 右值引用不能绑左值(为了区分
int &&b = x+0; // ok
void fun(int &lref) {
cout << "l-value" << endl;
}
void fun(int &&rref) {
cout << "r-value" << endl;
}
int main() {
int x = 10;
fun(x); // l value
fun(10); // r value
}
没有接受右值的函数时这样的函数也能接受右值
Move Constructor¶
移动拷贝构造函数:参数为右值引用的拷贝构造函数。
如果有一个对象,里面有指针指向一块内存。拷贝构造就是重新申请一块内存并将原内存的数据拷贝过来。而移动构造就是让新对象的指针指向内存,但原指针不再指向这个内存(nullptr
).
DynamicArray(DynamicArray&& rhs) : m_size{rhs.m_size}, m_array{rhs.m_array}
{
rhs.m_size = 0;
rhs.m_array = nullptr;
cout << "Move constructor: dynamic array is moved!\n";
}
什么时候需要移动构造?
类内有指针,而且对象会在函数内传进传出。
std::move()
std::move
将 v1 转化为右值,从⽽激发 v3 的移动构造函数,实现移动语义* 对象初始化的形式
//小括号初始化
string str("hello");
//等号初始化
string str = "hello";
//大括号初始化
struct Studnet
{
char *name;
int age;
};
Studnet s = {"dablelv", 18};//Plain of Data类型对象
Studnet sArr[] = {{"dablelv", 18}, {"tommy", 19}}; //POD数组
class Test
{
int a;
int b;
public:
Test(int i, int j);
};
Test t{0, 0}; //C++11 only,相当于 Test t(0,0);
Test *pT = new Test{1, 2}; //C++11 only,相当于 Test* pT=new Test(1,2);
int *a = new int[3]{1, 2, 0}; //C++11 only
Delegating Ctor