避免对象切片并使用shared_ptr

时间:2015-06-25 04:50:41

标签: c++ c++11

现在我正在开发一个搜索节点程序。我的代码如下,我实际上想要搜索特定的子节点。为了避免对象切片,我使用了一个指针,但指针可能是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

3 个答案:

答案 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。至少不是现在写的。目前,child1child2Manager所有,因为它们是按值的成员变量。除了Manager之外,您不希望其他任何人删除它们。

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

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

                      }