[英]Avoiding object slicing and using shared_ptr
现在,我正在开发一个搜索节点程序。 我的代码在下面,我实际上想搜索特定的子节点。 为避免对象切片,我使用了一个指针,但该指针可能为null_ptr。 所以我应该如何避免这个问题。 我不确定它的问题吗? 为了避免新的实例属性,自c ++ 11起,应将main()中的Parent obj实例声明为shared_ptr受支持?
#include <iostream>
using namespace std;
class Parent {
public:
Parent() { name = "Parent"; }
virtual void print() {
cout << "Parent::print()" << endl;
}
string name;
};
class Child1 : public Parent {
public:
Child1() { name = "Child1"; }
virtual void print() override{
cout << "Child1::print()" << endl;
}
};
class Child2 : public Parent {
public:
Child2() { name = "Child2"; }
virtual void print() override{
cout << "Child2::print()" << endl;
}
};
class Manager {
public:
void getChild(int i, Parent* obj) {
if(i==0) {
cout << "sest child1" << endl;
obj = (&child1);
} else {
cout << "sest child2" << endl;
obj = (&child2);
}
}
Child1 child1;
Child2 child2;
};
int main()
{
// object slicing?
// Parent obj;
// Manager manager;
// manager.getChild(1, obj);
// obj.print();
Parent* obj;
Manager manager;
manager.getChild(1, obj);
obj->print();
}
我的代码由于分段错误而损坏。
$ a.out D
sest child2
[1] 5457 segmentation fault ./work/derived/a.out D
void getChild(int i, Parent* obj)
该函数签名告诉我们该函数采用整数和指向对象的指针。 两者都按值传递:
如果您有一个对象,在地址42处说,那么您将有效地将数字42作为第二个参数传递。
在函数内部,参数充当变量,并且由于在这种情况下它们不是const,因此可以对其进行修改。 所以当你写
obj = &child1;
您只更改了局部变量的状态,例如从前面提到的42更改为其他地址,例如24。这都不会影响调用方作为第二参数给出的指针。
您想要实现的是拥有一个“输出”参数。 为此,您需要一个指向要更改的对象的指针。 在您的情况下,您想更改一个指针,因此需要一个指向指针的指针:
void getChild(int i, Parent * * obj_ptr)
要将其设置为值,您需要取消引用:
*obj_ptr = &child1;
但是在这种情况下,我不会使用out参数,因为您能够从函数中返回值,所以只需返回指针即可:
Parent* getChild(int i) {
// ...
return &child1;
}
// in main
Parent* node = manager.getChild(1);
关于std::shared_ptr
:您应该考虑谁拥有您的指针指向的实例。 也就是说,谁负责破坏它们并释放关联的内存。 在您的情况下,子实例是管理器实例的一部分。 因此,经理实例拥有它们并会照顾它们。 shared_ptr
用于(希望)罕见的情况,当您不完全知道实例需要多长时间时。 因此,您共享所有权,使最后的所有者负责销毁和释放。
对我来说,您实际上需要什么样的所有权尚不完全清楚。 您是否需要某种遍历,即其他孩子的父母(或您的术语管理者)的孩子实例? 还是只需要访问成员? 然后,除了引用之外,我什么都不会使用指针。
您正在按值将指针obj
传递给getChild
(获取副本)。 如果您将本地obj
指针更改为指向不更改主范围内obj
指针的其他obj
。 主作用域中的obj
指针仍然指向垃圾。
因为obj
是out参数,所以写getChild
的更惯用的方式是使用返回值:
Parent* getChild(int i) {
if(i==0) {
cout << "sest child1" << endl;
return &child1;
}
cout << "sest child2" << endl;
return &child2;
}
您可以这样使用:
Manager manager;
auto obj = manager.getChild(1);
obj->print();
不,您不应该在主作用域中为obj
使用shared_ptr
。 至少不是现在写的那样。 当前, child1
和child2
由Manager
拥有,因为它们是按值成员变量。 除了Manager
您不希望其他人删除它们。
但这也许不是您想要的设计。 也许您打算使用类似于“ 原型模式”的东西,其中getChild
返回应该由调用者拥有的child1
或child2
的克隆。
我认为您不应该在堆栈上分配child1和child2,而应该使用new运算符在头上分配它。第二个修改是在一些不同的函数中获取任何新的内存分配时,用户指针指向指针,修改后的程序如下:
#include <iostream>
#include <string>
using namespace std;
class Parent {
public:
Parent() { name = "Parent"; }
virtual void print() {
cout << "Parent::print()" << endl;
}
string name;
};
class Child1 : public Parent {
public:
Child1() { name = "Child1"; }
virtual void print() {
cout << "Child1::print()" << endl;
}
};
class Child2 : public Parent {
public:
Child2() { name = "Child2"; }
virtual void print() {
cout << "Child2::print()" << endl;
}
};
class Manager {
public:
Manager()
{
child1 = new Child1;
child2 = new Child2;
}
void getChild(int i, Parent** obj) {
if(i==0) {
cout << "sest child1" << endl;
*obj = child1;
} else {
cout << "sest child2" << endl;
*obj = child2;
}
}
Child1 *child1;
Child2 *child2;
};
int main()
{
Parent* obj;
Manager manager;
manager.getChild(1, &obj);
obj->print();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.