C++ - 复制(copy) 和 虚复制(virtual copy) 的 区别

本文详细解析了C++继承过程中的复制(copy)与虚复制(virtual copy)概念,通过实例展示了如何避免复制导致的派生对象部分丢失问题,并介绍了使用虚函数clone与shared_ptr进行动态绑定的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

复制(copy) 和 虚复制(virtual copy) 的 区别

 

本文地址: http://blog.csdn.net/caroline_wendy/article/details/16120397

 

在继承过程中, 需要区分复制(copy)虚复制(virtual copy);

在派生类转换为基类时, 复制(copy)有可能切掉(sliced down)派生对象的派生部分, 只保留基类部分, 使派生类的虚函数无法使用;

为了避免此情况, 如果传入是对象, 则可以定义虚函数clone, 使派生类继承此虚函数, 再传入容器, 可以重载方法;

另外, 如果传入是实参, 使用shared_ptr<Base>配合make_shared<Derived>添加容器, 也可以进行动态绑定;

再传入容器中, 容器会自动调用派生类的重载方法, 实现动态绑定;

注意: 引用限定符(reference qualifier) GCC 4.8.1 才能支持;

 

代码:

/*
 * CppPrimer.cpp
 *
 *  Created on: 2013.11.12
 *      Author: Caroline
 */

/*eclipse cdt*/

#include <iostream>
#include <string>
#include <vector>
#include <set>

#include <utility>
#include <memory>
#include <cstddef>

using namespace std;

class Quote;

class Quote {
public:
	Quote() = default;
	Quote (const std::string& book, double sales_price) :
		bookNo (book), price (sales_price) {}
	void add_item (const Quote& sale);
	std::string isbn() const { return bookNo; }
	virtual double net_price (std::size_t n) const { return n* price; } //虚函数
	virtual Quote* clone() const & {return new Quote(*this);}
	virtual Quote* clone() && {return new Quote(std::move(*this));}
	virtual ~Quote() = default; //动态绑定析构器
private:
	std::string bookNo;
protected: //受保护类型
	double price = 0.0;
};

class Disc_quote : public Quote { //抽象基类
public:
	Disc_quote() = default;
	Disc_quote (const std::string& book, double price, std::size_t qty, double disc) :
		Quote(book, price), quantity (qty), discount (disc) {}
	double net_price (std::size_t) const = 0; //纯虚函数
protected:
		std::size_t quantity = 0;
		double discount = 0.0;
};

class Bulk_quote final : public Disc_quote { //final限定词, 无法被继承
public:
	Bulk_quote() = default;
	Bulk_quote(const std::string& book, double p, std::size_t qty, double disc) :
		Disc_quote(book, p, qty, disc) {} //使用基类的构造器
	double net_price(std::size_t cnt) const override;
	virtual Bulk_quote* clone() const & {return new Bulk_quote(*this);}
	virtual Bulk_quote* clone() && {return new Bulk_quote(std::move(*this));}
};

double Bulk_quote::net_price(std::size_t cnt) const
{
	if (cnt >= quantity)
		return cnt * (1-discount) * price;
	else
		return cnt * price;
}

double print_total(std::ostream &os, const Quote& item, std::size_t n)
{
	double ret = item.net_price(n);
	os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << std::endl;
	return ret;
}

class Basket {
public:
	void add_item (const std::shared_ptr<Quote> &sale) { items.insert(sale); }
	void add_item (const Quote& sale)
	{
		//items.insert(std::shared_ptr<Quote>(new Quote(sale))); //不会动态绑定
		items.insert(std::shared_ptr<Quote>(sale.clone()));
	}
	void add_item (Quote&& sale)
	{
		//items.insert(std::shared_ptr<Quote>(new Quote(std::move(sale)))); //不会动态绑定
		items.insert(std::shared_ptr<Quote>(std::move(sale).clone()));
	}
	double total_reciept (std::ostream&) const;
private:
	static bool compare (const std::shared_ptr<Quote> &lhs,
			const std::shared_ptr<Quote> &rhs)
	{ return lhs->isbn() < rhs->isbn(); };
	std::multiset<std::shared_ptr<Quote>, decltype(compare)*> items{compare};
};

double Basket::total_reciept(std::ostream &os) const
{
	double sum = 0.0;
	for(auto iter = items.cbegin(); iter != items.cend();
			iter = items.upper_bound(*iter)) { //跳过同名书, 直接计算count
		sum += print_total(os, **iter, items.count(*iter)); //*it是shared_ptr; **it是object
	}
	os << "Total Sale: " << sum << std::endl;
	return sum;
}

int main (void) {

	Basket bsk;
	/*bsk.add_item(std::make_shared<Quote>("CppPrimer", 45));
	bsk.add_item(std::make_shared<Bulk_quote>("EffectiveCpp", 50, 2, 0.15));
	bsk.add_item(std::make_shared<Bulk_quote>("EffectiveCpp", 50, 2, 0.15));
	bsk.add_item(std::make_shared<Bulk_quote>("EffectiveCpp", 50, 2, 0.15));*/

	bsk.add_item(Quote("CppPrimer", 45));
	bsk.add_item(Bulk_quote("EffectiveCpp", 50, 2, 0.15));
	bsk.add_item(Bulk_quote("EffectiveCpp", 50, 2, 0.15));
	bsk.add_item(Bulk_quote("EffectiveCpp", 50, 2, 0.15));

	bsk.total_reciept(std::cout);

	return 0;

}


输出:

ISBN: CppPrimer # sold: 1 total due: 45
ISBN: EffectiveCpp # sold: 3 total due: 127.5
Total Sale: 172.5


\

 

 

### 关于C++类中的复制构造函数 #### 定义与基本语法 在C++中,复制构造函数是一种特殊类型的构造函数,用于通过另一个同类型的对象来初始化新创建的对象。它的标准形式如下所示[^1]: ```cpp ClassName::ClassName(const ClassName& other) { // 初始化逻辑 } ``` 此函数的特点包括: - 函数名称与类名一致。 - 只接受一个参数,该参数是一个指向当前类的常量引用。 如果没有显式定义复制构造函数,则编译器会自动生成一个默认版本[^3]。然而,默认实现仅执行浅拷贝(shallow copy),即简单地将源对象的数据成员逐个赋值给目标对象的数据成员[^5]。 #### 自定义复制构造函数的意义 尽管编译器能够生成默认的复制构造函数,但在涉及动态内存分配或其他资源管理的情况下,可能需要开发者自行定义以确保深拷贝(deep copy)。例如,在下面的例子中展示了如何处理指针型数据成员: ```cpp class MyClass { public: int* data; // 默认构造函数 MyClass() : data(new int(0)) {} // 复制构造函数 MyClass(const MyClass& other) { data = new int(*other.data); // 创建新的堆空间并复制原始值 } ~MyClass() { delete data; } }; ``` 在此案例中,`data` 是一块动态分配的整数存储区域;如果不采用深拷贝策略,两个对象可能会共享同一块内存地址,从而引发未定义行为或者双重释放错误等问题。 #### 复制构造函数的应用场景 除了常规意义上的复制构造函数外,还存在所谓的“复制构造函数”,主要用于解决多态环境下的对象克隆需求。比如在一个基类及其多个派生类构成的继承体系里,为了能够在运行时期间正确实例化具体子类型而非仅仅限于基类本身,可以借助工厂模式配合纯方法达成目的[^2]: ```cpp #include <iostream> using namespace std; // 基类声明 class Base { protected: string name; public: explicit Base(string n):name(n){} virtual unique_ptr<Base> clone() const=0; // 纯函数强制派生类实现自己的clone机制 void showName(){ cout << "I am a "<<name<<endl; }; }; // 派生类A class DerivedA final: public Base{ private: double value; public: DerivedA(double v,string n="Derived A"):Base(n),value(v){} unique_ptr<Base> clone()const override{ return make_unique<DerivedA>(*this);} }; int main(){ auto obj=new DerivedA(3.14); obj->showName(); auto clonedObj=obj->clone(); clonedObj->showName(); delete obj; return 0; } ``` 在这个例子里面,我们看到即使是从基类指针出发调用了 `clone()` 方法,最终得到的结果仍然是确切对应的派生类别副本——这正是得益于所谓“复制构造”的设计理念支持。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ManonLegrand

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值