提问人:Pravej Khan 提问时间:8/4/2022 最后编辑:Mark RotteveelPravej Khan 更新时间:8/13/2022 访问量:168
C++ 中类的复制构造函数
Copy constructor for the class in C++
问:
我想为该类编写一个复制构造函数。Plane
class Widget{ };
class Button: public Widget{};
class Label: public Widget {};
class Plane {
vector<Widget*>vec;
public:
void Add (Widget* w) {
vec.push_back(w);
}
// need to implement copy constructor for this class???
//Plane (const Plane &obj) ???
~Plane() {
for (auto w: vec) {
delete w;
}
}
};
int main () {
Plane p1;
p1.Add(new Button);
p1.Add(new Label);
Plane p2(p1);
return 0;
}
需要您的帮助来编写 的复制构造函数。我还需要更改某些内容吗?我想要一个深拷贝,这就是为什么这里需要一个复制构造函数。我尝试自己编写复制构造函数,但失败了。class Plane
Widget class
答:
2赞
Giogre
8/7/2022
#1
我不知道你为什么决定使用原始指针而不是 s。unique_ptr
unique_ptr
具有 RAII 功能,这意味着当超出范围时,它们会自动销毁指向的资源以及指针。
相反,使用原始指针时,您需要手动使用它们。此外,如果将它们用作自定义类中的数据成员,则还需要定义自己的&构造函数和赋值运算符。delete
copy
move
一种方法(我听说在Java中是臭名昭著的,在C++中更不用说了)将允许您根据代码的需要执行深度复制。clone()
vector<Widget*>
如果按如下方式修改,该程序应该可以工作:
#include <iostream>
#include <vector>
#include <utility>
#include <stdexcept>
using std::cout;
using std::vector;
using std::move;
using std::exception;
using std::logic_error;
class Widget
{
public:
Widget() {cout << "created Base Widget\n";}
Widget(const Widget& original) noexcept {
cout << "copied Base Widget\n";
}
virtual ~Widget(){} // need virtual destructor with class hierarchy
virtual Widget* clone() const { // makes deep copies of pointers
if constexpr(noexcept(Widget(*this))) {
try {
cout << "cloned Widget\n";
return new Widget(*this);
}
catch(exception& e) {
throw runtime_error("error occurred while cloning Widget");
}
} else
throw logic_error("Widget: Faulty constructor");
}
};
class Button: public Widget{
public:
Button(): Widget() { cout << "created Button\n"; }
Button(const Button& original) noexcept: Widget(original){
cout << "copied Button\n";
}
virtual Button* clone() const {
if constexpr(noexcept(Button(*this))) {
try {
cout << "cloned Button\n";
return new Button(*this);
}
catch(exception& e) {
throw runtime_error("error occurred while cloning Button");
}
} else
throw logic_error("Button: Faulty constructor");
}
virtual ~Button(){}
};
class Label: public Widget {
public:
Label(): Widget() { cout << "created Label\n"; }
Label(const Label& original) noexcept: Widget(original){
cout << "copied Label\n";
}
virtual Label* clone() const {
if constexpr(noexcept(Label(*this))) {
try {
cout << "cloned Label\n";
return new Label(*this);
}
catch(exception& e) {
throw runtime_error("error occurred while cloning Label");
}
} else
throw logic_error("Label: Faulty constructor");
}
virtual ~Label(){}
};
class Plane { // destructor is needed so copy&move semantics are also needed
// (rule of 5)
vector<Widget*>vec;
public:
Plane(): vec(0) {}
Plane(const Plane& original) // performs deep copy
{
vec.resize(original.vec.size());
for (size_t i = 0; i != original.vec.size(); ++i)
vec[i] = original.vec[i]->clone();
}
Plane& operator=(const Plane& original)
{
if (this != &original) {
vec.resize(original.vec.size());
for (size_t i = 0; i != original.vec.size(); ++i)
vec[i] = original.vec[i]->clone();
}
return *this;
}
~Plane() {
for (auto w: vec) {
delete w;
cout << "deleted Widget\n";
}
}
// for completeness also add move semantics
Plane(Plane&& original) noexcept
{
vec = move(original.vec);
cout << "moved Plane\n";
}
Plane& operator=(Plane&& original) noexcept
{
if (this != &original)
vec.swap(original.vec);
return *this;
}
void Add (Widget* w) {
vec.push_back(w);
cout << "added Widget*\n";
}
};
int main () {
Plane p1;
p1.Add(new Button);
p1.Add(new Label);
Plane p2(p1);
return 0;
}
结果符合预期:四个 s 是 d(两个 in 和 2 个 inWidget
delete
p1
p2
)
created Base Widget
created Button
added Widget*
created Base Widget
created Label
added Widget*
cloned Button
copied Base Widget
copied Button
cloned Label
copied Base Widget
copied Label
deleted Widget
deleted Widget
deleted Widget
deleted Widget
评论
2赞
user17732522
8/13/2022
否则,这也不是例外安全的。如果其中一个克隆元素的分配/构造发生,则已经克隆的元素将被泄露。std::unique_ptr
2赞
user17732522
8/13/2022
也是一个小问题,但并不完全正确。如果倒计时,则使用 可以根据 推断出正确的类型,但这始终可以推断出哪个类型可能太小而无法容纳大小,在这种情况下,它将溢出未定义的行为。auto i = 0
auto
size
auto
int
1赞
Giogre
8/13/2022
@user17732522 谢谢。添加了构造,并将该循环索引更改为 。try ... catch
clone()
auto
size_t
3赞
user17732522
8/13/2022
不幸的是,/没有做任何事情来真正解决问题。如果存在例外,则副本将不完整。正确添加异常安全需要在循环调用中捕获异常,然后遍历索引小于当前的所有元素以调用它们,然后重新抛出异常。因为这相当复杂,所以不应该手动完成。如果与 和 一起使用,而不是从一开始就使用,那么这将自动正常工作。try
catch
for
clone
i
delete
std::vector
std::unique_ptr
std::make_unique
new
1赞
user17732522
8/13/2022
如果溢出,则不会引发异常。循环中的溢出增量此时将具有未定义的行为。但是您使用 正确地解决了该问题。相关分配总是有两个版本,一个是另一个不是。没有放置参数的表达式将始终使用抛出参数。但不仅分配可以抛出。构造函数也可以(不是在这里,而是在典型情况下)也可以抛出。original.vec.size()
int
size_t
operator new
noexcept
new
std::nothrow
评论
virtual Widget* Widget::Clone();
Plane
Widget
层次结构会遗漏虚拟析构函数和其他虚拟函数。没有这些,你就无法工作。vector<Widget*>