【问题标题】:Declare an object as abstract class将对象声明为抽象类
【发布时间】:2021-06-10 17:45:33
【问题描述】:

编辑最初发布的这个问题是我所拥有的问题的简化版本,因此不包含导致错误的问题。我已经更新为更像我的问题,并且会发布答案以防其他人遇到类似问题。

在 C++ 中是否可以将对象声明为抽象类,然后将其实例化为派生类?

以这个修改版的示例代码为例,来自https://www.tutorialspoint.com/cplusplus/cpp_interfaces.htm

class Shape {
   public:
      // pure virtual function providing interface framework.
      virtual int getArea() = 0;
      virtual int getNumOfSides() = 0;
      void setWidth(int w) {
         width = w;
      }
   
      void setHeight(int h) {
         height = h;
      }
   
   protected:
      int width;
      int height;
};
 
// Derived classes
class Rectangle: public Shape {
   public:
      int getArea() { 
         return (width * height); 
      }
};

class Triangle: public Shape {
   public:
      int getArea() { 
         return (width * height)/2; 
      }
};
 
int main(void) {
   Rectangle Rect;
   Triangle  Tri;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
   
   // Print the area of the object.
   cout << "Total Rectangle area: " << Rect.getArea() << endl;

   Tri.setWidth(5);
   Tri.setHeight(7);
   
   // Print the area of the object.
   cout << "Total Triangle area: " << Tri.getArea() << endl; 

   return 0;
}

但是,如果我们在编译时不知道 Shape 的类型,是否可以这样做:

Shape *shape;

if (userInput == 'R') {
   shape = new Rectangle();
} else if (userInput == 'T') {
   shape = new Triangle();
}

// etc.

... 可以在 C# 中完成吗?

我试过了,但是报错了:

错误:抽象类类型“矩形”的无效新表达式

这是在 QT 内。

【问题讨论】:

  • Shape* shape;
  • 在 C# 中,像 shape 这样的标识符基本上是一个智能指针。在 C++ 中,对象的实例和指向对象的指针之间有一个非常重要的区别。 Shape shape; 试图定义一个完整的 Shape 对象,它不能(它是抽象的)。你想要一个指向Shape 的指针。请考虑使用std::unique_ptr&lt;Shape&gt; shape;
  • 哪个编译器给了你这个错误?这很奇怪,因为Rectangle 不是抽象的。当我尝试它时,我得到了相当不同的错误消息:error: cannot declare variable 'shape' to be of abstract type 'Shape'(定义 shape 时预期)和 error: no match for 'operator=' (operand types are 'Shape' and 'Rectangle*')(分配 new Rectangle 时)和 error: no match for 'operator=' (operand types are 'Shape' and 'Triangle*')(分配new Triangle时)。后面的消息中的星号非常重要。
  • 抱歉,我应该包含细节,这是 QT,我已将其声明为指针 - 我已经编辑了帖子。
  • 我要坦白,我没有发布完整的代码,这是一个高度简化的版本,为了避免共享机密代码和简单起见。实际的代码是不同的,并且包含了一个没有的问题,这使得这个问题无法回答。

标签: c++ qt oop abstract-class


【解决方案1】:

问题在于,并非所有抽象类定义的虚函数都在派生类中实现。

我需要

// Derived classes
class Rectangle: public Shape {
   public:
     int getArea() { 
        return (width * height); 
     }
     int getNumOfSides() {
        return 4;
     }
};

class Triangle: public Shape {
   public:
     int getArea() { 
        return (width * height)/2; 
     }
     int getNumOfSides() {
        return 3;
     }
};

【讨论】:

    【解决方案2】:

    在 C++ 中是否可以将对象声明为抽象类,然后将其实例化为派生类?

    你不能声明一个对象,不。抽象类不能被实例化。但是,您可以声明一个 reference/pointer 指向实现抽象类的对象,是的。例如:

    Shape *shape;
    
    if (userInput == 'R') {
       shape = new Rectangle();
    } else if (userInput == 'T') {
       shape = new Triangle();
    }
    
    // etc.
    
    delete shape;
    

    在 C++11 及更高版本中,当指针超出范围时,您可以使用std::unique_ptrstd::shared_ptr 自动为您调用delete,例如:

    std::unique_ptr<Shape> shape;
    
    if (userInput == 'R') {
       shape.reset(new Rectangle);
       // or: shape = std::unique_ptr<Shape>(new Rectangle);
       // or: shape = std::make_unique<Rectangle>(); // C++14 and later only
    } else if (userInput == 'T') {
       shape.reset(new Triangle);
       // or: shape = std::unique_ptr<Shape>(new Triangle);
       // or: shape = std::make_unique<Triangle>(); // C++14 and later only
    }
    
    // etc.
    
    std::shared_ptr<Shape> shape;
    
    if (userInput == 'R') {
       shape.reset(new Rectangle);
       // or: shape = std::make_shared<Rectangle>();
    } else if (userInput == 'T') {
       shape.reset(new Triangle);
       // or: shape = std::make_shared<Triangle>();
    }
    
    // etc.
    

    无论哪种方式,只要确保Shape 有一个virtual 析构函数,这样当delete'通过Shape* 指针指向派生对象时,就会调用正确的派生析构函数:

    class Shape {
       public:
          virtual ~Shape() {}
       // ...
    }; 
    

    【讨论】:

      【解决方案3】:

      如果你想保持你的程序内存安全,你可以这样使用std::shared_ptr

      #include <memory>
      
      template <class T>
      using ptr = std::shared_ptr<T>;
      
      class Shape { ... };
      class Rectangle: public Shape { ... };
      class Triangle: public Shape { ... };
       
      int main() 
      {
          ptr Rect = std::make_shared<Rectangle>();
          ptr Tri = std::make_shared<Triangle>();
      
          // Notice you have to use '->' instead of '.', since those are (smart) pointers
          Rect->setWidth(5);
          Rect->setHeight(7);
      
          cout << "Total Rectangle area: " << Rect->getArea() << endl;
      
          Tri->setWidth(5);
          Tri->setHeight(7);
      
          cout << "Total Triangle area: " << Tri->getArea() << endl; 
      
          // Can't use CTAD as for Rect and Tri. We have to specify 'Shape'.
          ptr<Shape> shape;
      
          char userInput;
          std::cin >> userInput;
          if (userInput == 'R') {
              shape = std::make_shared<Rectangle>();
          } else if (userInput == 'T') {
              shape = std::make_shared<Triangle>();
          }
      
          cout << shape->getArea() << endl;
      
          return 0;
      }
      

      【讨论】:

      • 为什么是shared_ptr 而不是unique_ptr?在此示例中,Shape 指针实际上并未被共享,因此 shared_ptr 是不必要的开销。
      • 如果您需要多态性,通常最好使用 shared_ptr。
      • unique_ptr 适用于多态类型。 shared_ptr 相对于 unique_ptr 的唯一优势是引用计数,而这不是多态性所必需的。
      • 当然,直到他需要将指针复制到其他地方或使用one of those。强制 unique_ptr 作为 shared_ptr 的默认选择只是过早的优化。
      • 如果你需要保存对一个对象的多个引用,那么显然你必须使用shared_ptr而不是unique_ptr。不过,更多时候,unique_ptr 就足够了。我只是说,shared_ptr 通常不需要使用多态性。两个ptrs 都使用多态性,并且都有其特定的用例。
      猜你喜欢
      • 2020-04-12
      • 2011-05-13
      • 2014-02-26
      • 1970-01-01
      • 1970-01-01
      • 2012-07-23
      • 2015-02-28
      • 1970-01-01
      • 2017-08-17
      相关资源
      最近更新 更多