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

  • Expert C++
  • Vardan Grigoryan Shunguang Wu
  • 547字
  • 2021-06-24 16:34:01

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.

主站蜘蛛池模板: 西宁市| 霍林郭勒市| 恩施市| 大石桥市| 绩溪县| 宜城市| 衢州市| 南汇区| 新余市| 资溪县| 抚顺市| 乌兰察布市| 驻马店市| 丹巴县| 永康市| 南丹县| 龙陵县| 大新县| 朝阳县| 将乐县| 台南市| 信阳市| 新干县| 隆子县| 绩溪县| 凤庆县| 湟中县| 于都县| 蒙山县| 榆中县| 成都市| 卢氏县| 永福县| 岚皋县| 邳州市| 新乡县| 乌恰县| 神农架林区| 连云港市| 乐至县| 永靖县|