2015-06-25 57 views
1

现在我正在开发一个搜索节点程序。我的代码在下面,我实际上想要搜索特定的子节点。为了避免对象切片,我使用了一个指针,但指针可能是null_ptr。所以我应该如何避免这个问题。我不确定它的问题?并避免新实例属性,父obj在main()中的实例应该声明为自C++ 11以来支持的shared_ptr?避免对象切片并使用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 
+1

您正在通过值'getChild'将指针'obj'传递给'getChild'。这不会影响'main'范围内'obj'的值,它仍然未初始化。它与对象切片无关。 **阅读你的编译器警告**。 –

+0

您需要将指针的地址传递给'getChild',即指向指针的指针。 –

回答

0

您是按值传递指针objgetChild(取复印件)。如果将本地obj指针更改为指向主范围内不改变obj指针的其他对象。主范围中的obj指针仍然指向垃圾。

由于obj是一个不折不扣的参数写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。至少不像现在这样写。目前,child1child2Manager拥有,因为它们是按值成员变量。您不希望其他人删除Manager以外的其他人。

但也许这不是你想要的设计。也许你打算更像Prototype Pattern,其中getChild返回child1child2的副本,该副本应该由调用方拥有。

1
void getChild(int i, Parent* obj) 

此函数签名告诉我们该函数接受一个整数并指向一个对象。两者都通过值:

如果你有一个对象,说在地址42,那么你会有效地通过数字42作为第二个参数。

函数内部的参数作为变量,因为它们在这种情况下不是常量,所以可以修改它们。所以,当你写

obj = &child1; 

你只改变了本地变量的状态,从上述42说一些其他的地址,例如24.这些都不影响调用者作为第二参数给出的指针。

你想实现的是有一个“out”参数。为此,你需要一个指向你想改变的东西的指针。你的情况,你想改变一个指针,所以你需要一个指针的指针:

void getChild(int i, Parent * * obj_ptr) 

要将其设置为一个值,则需要取消对它的引用:

*obj_ptr = &child1; 

这种情况下,我不会用一个输出参数,因为你能够返回从函数的值,所以只返回指针:

Parent* getChild(int i) { 
    // ... 
    return &child1; 
} 
// in main 
Parent* node = manager.getChild(1); 

浓erning std::shared_ptr你应该考虑谁拥有指针指向的实例。也就是说,谁负责破坏他们并释放相关的记忆。在你的情况下,子实例是管理器实例的一部分。所以经理实例拥有它们并且会照顾它们。 A shared_ptr适用于(希望)罕见的情况,如果您不确切知道实例需要多长时间。所以你分享了所有权,让最后的所有者负责破坏和释放。

你实际需要什么样的所有权并不完全清楚。你想要某种遍历,也就是说,是其他孩子的孩子的父母(或您的术语中的管理者)?或者你只需​​要访问成员?然后我不会使用指针,除了引用。

+0

谢谢!我意识到它使用双指针。虽然我的示例getChild返回void,但实际代码返回布尔值来检查函数结果。如果我使用双指针不直接返回指针,是否有任何缺点? – jef

+1

布尔返回值是否与指针的有效性相结合?那是不是有些“发现”的标志?然后,我会返回指针,并通过返回'nullptr'指示找不到。否则,出参数的工作效果很好,只有他们有时会笨拙地处理。 –

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(); 

         } 
+0

在这种情况下,使用'new'分配'child1'和'child2'的好处是什么? –

+0

避免在函数中传递栈中分配的地址对象。 –

+0

这些对象作为'Manager'上的成员变量分配在堆栈上,因此返回它们的地址应该没有问题。如果你使用'new'进行分配,那么你需要决定谁来负责'删除'它们。正如你写的那样,没有人承担这个责任,而你正在泄漏记忆。 –