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

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.

主站蜘蛛池模板: 江达县| 南投县| 南乐县| 宣武区| 五家渠市| 英超| 合川市| 漾濞| 法库县| 阳山县| 姜堰市| 孟州市| 沈阳市| 巩义市| 泰州市| 福鼎市| 安福县| 互助| 青冈县| 永昌县| 营口市| 湖北省| 莱芜市| 祁连县| 株洲市| 绥江县| 靖宇县| 金塔县| 崇州市| 宜宾市| 麻江县| 金门县| 荆门市| 莒南县| 阳曲县| 喀喇| 东乡族自治县| 成都市| 北辰区| 子洲县| 福州市|