代码之家  ›  专栏  ›  技术社区  ›  Hein Wessels

当派生对象声明没有新成员时是否会发生多态切片

  •  1
  • Hein Wessels  · 技术社区  · 4 年前

    如果存储派生类,是否在多态性中发生切片 按价值 , 但是 派生类没有额外成员?

    例如,如果我有一节课 Base . 但是我需要一个自定义构造函数,所以我实现了 Custom ,就像这样:

    class Base{
    public:
        int a;
        int b;
        Base(int a) 
            : a(a) 
        { b = 2; }
    };
    class Custom : public Base{
    public: // Note: no new members or functions!
        Custom(int bb) 
            : Base(1) 
        { b = bb; }
    };
    

    如果我用下面的代码运行它,它就会正常工作。这两个例子 底座 已正确初始化。他们

    #include <stdio.h>
    int main(){
        Base base = Base(1);
        Base sliced = Custom(2);
        Custom custom = Custom(2);
    
        // Some printing of `a`, `b`, and sizeof(base), etc
    }
    

    如您所料,它会打印出以下内容:

             |   a |   b | Size                                                         
    -----------------------------
        base |   1 |   2 |   8                                                          
      sliced |   1 |   2 |   8                                                          
      custom |   1 |   2 |   8   
    

    所以,如果我能更具体地问:

    1. 这会一直有效吗?一、 我是否会丢失 a b ? (委员会的任何成员/职能) 底座 )
    2. 如果没有,为什么不呢?派生对象是否存储额外信息?
    3. 如果这总是有效的话,这是坏习惯吗?

    我已经考虑过使用 strong types ,如所述 this FluentCpp博客。这也适用于我的实现,但我觉得这会使代码更加臃肿。

    2 回复  |  直到 4 年前
        1
  •  4
  •   R Sahu    4 年前

    如果按值存储派生类,但派生类没有额外的成员,那么切片是否发生在多态性中?

    对。

    这完全是另一回事,你没有看到任何重大损失,在你张贴的代码。您用来比较对象的度量表明,似乎没有任何东西因为对象切片而丢失。但是,如果您查看调试器中的对象,您很可能会注意到对象的类型不同。对象切片的效果在该环境中变得非常明显。

    我建议不要仅仅为了构造基类的对象而创建派生类。使用一个或多个非成员函数更合适。

    class Base
    {
       public:
          int a;
          int b;
          Base(int a, int b = 2) : a(a) , b(b) {}
    };
    
    Base fromA(int a)
    {
       return {a};
    }
    
    Base fromB(int b)
    {
       return {1, b};
    }
    
    
        2
  •  2
  •   cigien Jorge Eldis    4 年前

    将继承用于您似乎正在使用它的任务不是一个好主意。请注意,在c++20中,可以使用指定的初始值设定项解决所显示的问题:

    class Base 
    {
      public:
        int a = 1;
        int b = 2;
    };
    
    int main() 
    {
        Base a = Base{.a = 10};  // default b
        Base b = Base{.b = 20};  // default a
        Base c = Base{.a = 10, .b = 20};
    }
    
        3
  •  0
  •   Jeffrey    4 年前

    拥有派生类的主要原因是拥有虚函数。在代码中,一旦对象被切片,虚拟函数将开始调用基类版本:

    https://godbolt.org/z/WoT5qb

    #include <iostream>
    
    class Base{
    public:
        int a;
        int b;
        Base(int a) 
            : a(a) 
        {
            b = 2; 
        }
    
        virtual void foo()
        {       
            std::cout << "Base" << std::endl;
        }
    };
    class Custom : public Base{
    public: // Note: no new members!
        Custom(int bb) 
            : Base(1) 
        { b = bb; }
    
        virtual void foo()
        {       
            std::cout << "Custom" << std::endl;
        }
    };
    
    int main(){
        Base base = Base(1);
        Base sliced = Custom(2);
        Custom custom = Custom(2);
    
        base.foo();
        custom.foo();
        sliced.foo();
    }
    

    输出:

    Base
    Custom
    Base
    

    所以,切片仍然是非常不受欢迎的。