官术网_书友最值得收藏!

Copying objects

There are two kinds of copying: a deep copy and a shallow copy of objects. The language allows us to manage copy-initialization and the assignment of objects with the copy constructor and the assignment operator. This is a necessary feature for programmers because we can control the semantics of copying. Take a look at the following example:

Product p1;
Product p2;
p2.set_price(4.2);
p1 = p2; // p1 now has the same price
Product p3 = p2; // p3 has the same price

The line p1 = p2; is a call to the assignment operator, while the last line is a call to the copy constructor. The equals sign shouldn't confuse you regarding whether it's an assignment or a copy constructor call. Each time you see a declaration followed by an assignment, consider it a copy construction. The same applies to the new initializer syntax (Product p3{p2};).

The compiler will generate the following code:

Product p1;
Product p2;
Product_set_price(p2, 4.2);
operator=(p1, p2);
Product p3;
Product_copy_constructor(p3, p2);

The default implementation of the copy constructor (and assignment operator) performs a member-wise copy of objects, as shown in the following diagram:

Custom implementation is required in case the member-wise copy produces invalid copies. For example, consider the following copy of Warehouse objects:

class Warehouse {
public:
Warehouse()
: size_{0}, capacity_{1000}, products_{nullptr}
{
products_ = new Products[capacity_];
}

~Warehouse() {
delete [] products_;
}

public:
void add_product(const Product& p) {
if (size_ == capacity_) { /* resize */ }
products_[size_++] = p;
}
// other functions omitted for brevity

private:
int size_;
int capacity_;
Product* products_;
};

int main() {
Warehouse w1;
Product book;
Product apple;
// ...assign values to products (omitted for brevity)
w1.add_product(book);
Warehouse w2 = w1; // copy
w2.add_product(apple);
// something somewhere went wrong...
}

The preceding code declares two Warehouse objects, and two different products are then added to the warehouses. Though this example is somewhat unnatural, it shows the dangers of the default implementation of copying. The following illustration shows us what went wrong in the code:

Assigning w1 to w2 leads to the following structure:

The default implementation simply copies each member of w1 to w2. After copying, both products_ members of w1 and w2 point to the same location on the heap. When we add a new product to w2, the array pointed to by w1 is affected. It's a logical error that could lead to undefined behavior in the program. We need a deep rather than a shallow copy; that is, we need to actually create a new array of products that has a copy of w1's array. 

A custom implementation of the copy constructor and the assignment operator solves this issue of shallow copying:

class Warehouse {
public:
// ...
Warehouse(const Warehouse& rhs) {
size_ = rhs.size_;
capacity_ = rhs.capacity_;
products_ = new Product[capacity_];
for (int ix = 0; ix < size_; ++ix) {
products_[ix] = rhs.products_[ix];
}
}
// code omitted for brevity
};

The custom implementation of the copy constructor creates a new array. Then, it copies the source objects' array elements one by one, this way eliminating the product_ pointer from pointing to a wrong memory address. In other words, we implemented a deep copy of Warehouse objects by creating a new array.

主站蜘蛛池模板: 革吉县| 鸡西市| 万盛区| 应城市| 扶余县| 万年县| 松桃| 水城县| 昌邑市| 西城区| 星子县| 雅江县| 嵩明县| 霍山县| 临沭县| 黑水县| 萨嘎县| 滦平县| 民勤县| 建水县| 临潭县| 左云县| 镇安县| 三亚市| 从化市| 宜兴市| 平遥县| 绥滨县| 临汾市| 广宗县| 万盛区| 密山市| 龙山县| 和平区| 图木舒克市| 敦化市| 大石桥市| 景洪市| 南木林县| 太原市| 留坝县|