你如何在 C++ 中 return 一个接受纯虚拟(const ref)参数的对象?
How do you return an object that takes pure virtual (const ref) arguments in C++?
我有一个包含抽象 class 实例列表的对象。
class A { }; // abstract class
class B {
public:
void addA(const A& a) {
list_of_a_.push_back(&a);
}
private:
std::vector<const A*> list_of_a_;
};
A 是抽象的,所以我将它们作为指针存储在向量中,并将其作为常量引用传递。我有不同的 A.classes
然后我有一个类似工厂的函数来创建 B 和 returns 它。
B CreateB() {
B b;
DerivationOfA a; // subclass of abstract class A.
b.addA(a);
return b;
}
int main() {
B b = CreateB();
b.DoSomethingWithA(); // bad memory, 'a' was destroyed.
}
问题是一旦 B 从 "create" 函数返回,A 的实例就会在堆栈上被销毁。当我使用智能指针时也会发生同样的事情。我想避免使用常规指针来避免额外的内存管理问题。
这样做有什么技巧可以保留 CreateB
函数,避免使用指针,但仍然能够将抽象 classes 传递给 B
?我可以使用按值传递,只是 C++ 不允许使用抽象 classes.
编辑:我知道为什么会这样,但我不确定在使用纯虚拟对象时如何绕过它。除了我想避免的使用 C 指针之外,我不确定如何避免范围问题。
编辑2:纯虚拟classes而不是objects更准确。
编辑 3: 抽象 classes 而不是 纯虚拟 class满足挑剔。
CreateB
函数在堆栈上创建一个对象 b
,当我们从该函数 return 时,堆栈被展开并且 b
超出范围。
您可以在 main
函数中创建对象 b
,然后对该对象调用 addA
函数。
编辑
在堆上创建 b
并 return 引用它。
B* CreateB() {
B *b = new B;
// rest of the code
return b;
}
您的设计令人困惑。通过引用接收对象并保存它的指针是个坏主意。
- 使用智能指针。
- 如果你想通过指针保留对象,你需要在堆上分配它(通过
new
运算符)并在你的容器中释放(通过 delete
运算符)(class with矢量成员)。
- 如果你想通过引用传递它,你需要保证对象不会被删除。或者您需要在该函数调用期间复制该对象。
在一些库中,容器接收刚刚创建的对象指针并将其保存在内部数据中并负责删除。如果您不使用智能指针,则需要定义并记住哪个对象是指针引用的对象的主人。用指针管理对象不是简单的问题。智能指针让这更简单。
为 B 定义移动构造函数。
B factory() {
B x;
// some code
return std::move(x);
// return x; if with copy elision
}
应该可以。
如果移动语义不是一个选项。
struct B {
public:
class Holder {
public:
static Holder NewHolder() {
return Holder(new B{});
}
B *ptr_;
private:
Holder(B *ptr) : ptr_(ptr) {}
};
B() : resource_(new int) {
std::cout << "Construct" << std::endl;
}
B(Holder x) {
resource_ = x.ptr_->resource_;
x.ptr_->resource_ = nullptr;
delete x.ptr_;
}
~B() {
std::cout << resource_ << " deleted by " << this << std::endl;
delete resource_;
}
int *resource_;
};
B::Holder factory() {
B::Holder holder = B::Holder::NewHolder();
B &b = *holder.ptr_;
// some code
return holder;
}
int main() {
B b = factory();
return 0;
}
Holder 是一个嵌套的 class,它持有一个由 new 分配的 B。并且这个B不会被销毁,直到构造函数B(Holder x)
从持有者那里窃取资源,然后构造函数删除空B。
我有一个包含抽象 class 实例列表的对象。
class A { }; // abstract class
class B {
public:
void addA(const A& a) {
list_of_a_.push_back(&a);
}
private:
std::vector<const A*> list_of_a_;
};
A 是抽象的,所以我将它们作为指针存储在向量中,并将其作为常量引用传递。我有不同的 A.classes
然后我有一个类似工厂的函数来创建 B 和 returns 它。
B CreateB() {
B b;
DerivationOfA a; // subclass of abstract class A.
b.addA(a);
return b;
}
int main() {
B b = CreateB();
b.DoSomethingWithA(); // bad memory, 'a' was destroyed.
}
问题是一旦 B 从 "create" 函数返回,A 的实例就会在堆栈上被销毁。当我使用智能指针时也会发生同样的事情。我想避免使用常规指针来避免额外的内存管理问题。
这样做有什么技巧可以保留 CreateB
函数,避免使用指针,但仍然能够将抽象 classes 传递给 B
?我可以使用按值传递,只是 C++ 不允许使用抽象 classes.
编辑:我知道为什么会这样,但我不确定在使用纯虚拟对象时如何绕过它。除了我想避免的使用 C 指针之外,我不确定如何避免范围问题。
编辑2:纯虚拟classes而不是objects更准确。
编辑 3: 抽象 classes 而不是 纯虚拟 class满足挑剔。
CreateB
函数在堆栈上创建一个对象 b
,当我们从该函数 return 时,堆栈被展开并且 b
超出范围。
您可以在 main
函数中创建对象 b
,然后对该对象调用 addA
函数。
编辑
在堆上创建 b
并 return 引用它。
B* CreateB() {
B *b = new B;
// rest of the code
return b;
}
您的设计令人困惑。通过引用接收对象并保存它的指针是个坏主意。
- 使用智能指针。
- 如果你想通过指针保留对象,你需要在堆上分配它(通过
new
运算符)并在你的容器中释放(通过delete
运算符)(class with矢量成员)。 - 如果你想通过引用传递它,你需要保证对象不会被删除。或者您需要在该函数调用期间复制该对象。
在一些库中,容器接收刚刚创建的对象指针并将其保存在内部数据中并负责删除。如果您不使用智能指针,则需要定义并记住哪个对象是指针引用的对象的主人。用指针管理对象不是简单的问题。智能指针让这更简单。
为 B 定义移动构造函数。
B factory() {
B x;
// some code
return std::move(x);
// return x; if with copy elision
}
应该可以。
如果移动语义不是一个选项。
struct B {
public:
class Holder {
public:
static Holder NewHolder() {
return Holder(new B{});
}
B *ptr_;
private:
Holder(B *ptr) : ptr_(ptr) {}
};
B() : resource_(new int) {
std::cout << "Construct" << std::endl;
}
B(Holder x) {
resource_ = x.ptr_->resource_;
x.ptr_->resource_ = nullptr;
delete x.ptr_;
}
~B() {
std::cout << resource_ << " deleted by " << this << std::endl;
delete resource_;
}
int *resource_;
};
B::Holder factory() {
B::Holder holder = B::Holder::NewHolder();
B &b = *holder.ptr_;
// some code
return holder;
}
int main() {
B b = factory();
return 0;
}
Holder 是一个嵌套的 class,它持有一个由 new 分配的 B。并且这个B不会被销毁,直到构造函数B(Holder x)
从持有者那里窃取资源,然后构造函数删除空B。