现在我正在开发一个搜索节点程序。我的代码如下,我实际上想要搜索特定的子节点。为了避免对象切片,我使用了一个指针,但指针可能是null_ptr。那么我应该如何避免这个问题。我不确定它的问题?为了避免 new instance 属性,main()中的 Parent obj 实例应该被声明为自c ++ 11以来支持的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
答案 0 :(得分:1)
void getChild(int i, Parent* obj)
此函数签名告诉我们该函数采用整数和指向对象的指针。两者都按值传递:
如果你有一个物体,比如地址42,那你就可以有效地将数字42作为第二个参数。
在函数内部,参数充当变量,并且由于它们在这种情况下不是常量,因此您可以修改它们。所以当你写了
obj = &child1;
你只改变了一个局部变量的状态,比如说从前面提到的42到其他一些地址,例如24.这些都不会影响调用者作为第二个参数给出的指针。
你想要实现的是一个&#34; out&#34;参数。为此你需要一个指向你想要改变的东西的指针。在您的情况下,您想要更改指针,因此您需要指向指针的指针:
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
适用于(希望)极少数情况,因为您不知道实例需要多长时间。所以你分享了所有权,让最后的所有者负责破坏和释放。
您实际需要的所有权对我来说并不完全清楚。你想要某种遍历,即I.e.是其他孩子的子实例父母(或您的术语管理者)?或者您只需要访问会员?然后我根本不会使用指针。
答案 1 :(得分:0)
您正在将指针obj
传递给getChild
(按副本)。如果将本地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();
不,你不应该在主范围内使用shared_ptr
obj
。至少不是现在写的。目前,child1
和child2
归Manager
所有,因为它们是按值的成员变量。除了Manager
之外,您不希望其他任何人删除它们。
但也许这不是你想要的设计。也许您的意图更像Prototype Pattern,其中getChild
返回child1
或child2
的副本,该副本应由调用者拥有。
答案 2 :(得分:0)
我认为你不应该在堆栈上分配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();
}