繁体   English   中英

避免对象切片并使用shared_ptr

[英]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 至少不是现在写的那样。 当前, child1child2Manager拥有,因为它们是按值成员变量。 除了Manager您不希望其他人删除它们。

但这也许不是您想要的设计。 也许您打算使用类似于“ 原型模式”的东西,其中getChild返回应该由调用者拥有的child1child2的克隆。

我认为您不应该在堆栈上分配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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM