简体   繁体   English

避免对象切片并使用shared_ptr

[英]Avoiding object slicing and using shared_ptr

Now I'm developing a searching node program. 现在,我正在开发一个搜索节点程序。 My code is below and I actually want to search a specific child node. 我的代码在下面,我实际上想搜索特定的子节点。 To avoid object slicing, I used a pointer, but the pointer is null_ptr maybe. 为避免对象切片,我使用了一个指针,但该指针可能为null_ptr。 So how should I avoid this problem. 所以我应该如何避免这个问题。 I'm not sure its problem? 我不确定它的问题吗? And to avoid new instance attribute, Parent obj instance in main() should be declared as shared_ptr supported since c++11? 为了避免新的实例属性,自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();

 }

My code is broken because of segmentation fault. 我的代码由于分段错误而损坏。

  $ a.out D
    sest child2
    [1]    5457 segmentation fault  ./work/derived/a.out D
void getChild(int i, Parent* obj)

This function signature is telling us that the function takes an integer and pointer to an object. 该函数签名告诉我们该函数采用整数和指向对象的指针。 Both are passed by value: 两者都按值传递:

If you have an object, say at address 42, then you would effectively pass the number 42 as second parameter. 如果您有一个对象,在地址42处说,那么您将有效地将数字42作为第二个参数传递。

Inside the function the parameters act as variables, and since they're not const in this case, you can modify them. 在函数内部,参数充当变量,并且由于在这种情况下它们不是const,因此可以对其进行修改。 So when you wrote 所以当你写

obj = &child1;

you only changed the state of a local variable, say from the aforementioned 42 to some other address, eg 24. None of this is affecting the pointer the caller gave as second parameter. 您只更改了局部变量的状态,例如从前面提到的42更改为其他地址,例如24。这都不会影响调用方作为第二参数给出的指针。

What you wanted to achieve is having an "out" parameter. 您想要实现的是拥有一个“输出”参数。 For that you need a pointer to the thing you want to change. 为此,您需要一个指向要更改的对象的指针。 In your case you want to change a pointer, so you need a pointer to a pointer: 在您的情况下,您想更改一个指针,因此需要一个指向指针的指针:

void getChild(int i, Parent * * obj_ptr)

To set it to a value, you need to dereference it: 要将其设置为值,您需要取消引用:

*obj_ptr = &child1;

But in this case I wouldn't use an out parameter, since you're able to return a value from functions, so just return the pointer: 但是在这种情况下,我不会使用out参数,因为您能够从函数中返回值,所以只需返回指针即可:

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

concerning std::shared_ptr : You should think about who owns the instances your pointers point to. 关于std::shared_ptr您应该考虑谁拥有您的指针指向的实例。 That is, who is responsible of destructing them and freeing the associated memory. 也就是说,谁负责破坏它们并释放关联的内存。 In your case the child instances are part of the manager instance. 在您的情况下,子实例是管理器实例的一部分。 So the manager instance owns them and will take care of them. 因此,经理实例拥有它们并会照顾它们。 A shared_ptr is for the (hopefully) rare cases when you don't exactly know how long the instance is needed. shared_ptr用于(希望)罕见的情况,当您不完全知道实例需要多长时间时。 So you share the ownership, making the last owner responsible of destructing and freeing. 因此,您共享所有权,使最后的所有者负责销毁和释放。

What kind of ownership you actually need here is not entirely clear to me. 对我来说,您实际上需要什么样的所有权尚不完全清楚。 Do you want some kind of traversal, Ie are the child instances parents (or managers in your terminology) of other children? 您是否需要某种遍历,即其他孩子的父母(或您的术语管理者)的孩子实例? Or do you just need access to the members? 还是只需要访问成员? Then I wouldn't use pointers at all but references. 然后,除了引用之外,我什么都不会使用指针。

You are passing the pointer obj to getChild by value (taking a copy). 您正在按值将指针obj传递给getChild (获取副本)。 If you change the local obj pointer to point to a different object that doesn't change obj pointer in the main scope. 如果您将本地obj指针更改为指向不更改主范围内obj指针的其他obj The obj pointer in the main scope still points to garbage. 主作用域中的obj指针仍然指向垃圾。

As obj is an out parameter a more idiomatic way of writing getChild would be to use a return value: 因为obj是out参数,所以写getChild的更惯用的方式是使用返回值:

Parent* getChild(int i) {
  if(i==0) {
    cout << "sest child1" << endl;
    return &child1;
  }
  cout << "sest child2" << endl;
  return &child2;
}

Which you can use like this: 您可以这样使用:

Manager manager;
auto obj = manager.getChild(1);
obj->print();

And no, you shouldn't use a shared_ptr for obj in the main scope. 不,您不应该在主作用域中为obj使用shared_ptr At least not as it is written now. 至少不是现在写的那样。 Currently, child1 and child2 are owned by Manager as they are by-value member variables. 当前, child1child2Manager拥有,因为它们是按值成员变量。 You don't want anyone else deleting them other than Manager . 除了Manager您不希望其他人删除它们。

But perhaps that was not the design you intended. 但这也许不是您想要的设计。 Perhaps you intended something more like the Prototype Pattern where getChild returns a clone of child1 or child2 which should be owned by the caller. 也许您打算使用类似于“ 原型模式”的东西,其中getChild返回应该由调用者拥有的child1child2的克隆。

I think you shouldn't allocate child1 and child2 on stack , rather you should allocated it on head using new operator .Second modification would be to user pointer to pointer while getting any new memory allocation in some different function , Modified program is below : 我认为您不应该在堆栈上分配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