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

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.

主站蜘蛛池模板: 黔江区| 上饶市| 丹巴县| 建德市| 贵南县| 聂拉木县| 江口县| 溆浦县| 年辖:市辖区| 礼泉县| 桓台县| 集贤县| 阿尔山市| 台中县| 雷州市| 阳城县| 满城县| 奉新县| 金昌市| 原平市| 运城市| 于都县| 武鸣县| 铜川市| 方山县| 交城县| 克什克腾旗| 镇康县| 财经| 黄浦区| 平南县| 博白县| 会泽县| 达州市| 湖北省| 正蓝旗| 霞浦县| 侯马市| 安西县| 昭苏县| 德昌县|