【问题标题】:Abstract class accessing its child class functions抽象类访问其子类函数
【发布时间】:2021-05-09 15:45:25
【问题描述】:

是否可以作为父类从抽象类的指针访问子类的函数成员? 这是抽象类

class Item {
    public:
        virtual double getTotalPrice() = 0;
};

这是子类

class Product : public Item {
    protected:
        string name;
    public:
        Product() {}
        string getName() {}
        double getTotalPrice() {}
};

是否可以在主驱动程序中从 Item 类的实例访问此 getName() 函数,因为我还想显示总价和产品名称?

【问题讨论】:

  • @churill 因为这只是层次结构的粗略草图,并且名称必须作为此 Product 类的参数,因为 Product 还有其他参数。
  • 如果您无法在各种Item 上做某事,那么您需要重新考虑尝试在其中一些上做某事的设计。请考虑我们是否会关注meta.stackexchange.com/questions/66377/what-is-the-xy-problem 继续详细说明设计比开始解决由设计差距引起的问题要好得多。
  • @Yunnosch 我完全同意你关于重新设计的看法。但不幸的是,我不能在这个例子中。所以原来的代码只是 Product 类,它有名称、描述等这些参数,现在我需要添加一个抽象类作为父类,并且我不允许更改原始代码。
  • 好吧,你不能改变的类有 UB,因为应该返回一些东西的成员函数没有。
  • 但是只需要在Item 内部进行更改(您可以更改),而不是在Product 内部(您不能更改)。无论细节如何(包括您已经评论过的细节),请通过editing 将它们添加到问题中。他们很有帮助。

标签: c++ inheritance abstract-class


【解决方案1】:
  • 如果您应该能够通过基类指针 delete 对象,则您的基类需要有一个 virtual 析构函数。
  • 也制作getNamevirtual
class Item {
public:
    virtual ~Item() = default;
    virtual const string& getName() /* const */ = 0;
    virtual double getTotalPrice() /* const */ = 0;
};

请注意,不允许您更改的 Product 类存在轻微缺陷。

  • 它没有返回应该返回的值。
  • getter 成员函数不是const

如果您不能自己做,请提出一些更改:

class Product : public Item {
private: // prefer private member variables
    string name;
public:
    Product() = default;
    const string& getName() const override { return name; }
    double getTotalPrice() const override {
       // why is this implemented here when there's no price to return?
       return {};
    }
};

【讨论】:

  • 是的,我一开始就应该这样做,但我不确定是否允许这样做。谢谢!
  • 我理解您提出的更改,它们非常好,但这只是层次结构的草图,在实际程序中,函数有自己的主体并且是常量获取器,我很抱歉您是否感到困惑,如果可能的话,我只需要这个概念。但衷心感谢您!
  • @HermesÇati 好的,那我明白了:-)
【解决方案2】:

您不能拥有 abstract Item 类的实例(因为它是抽象的),但您可以拥有指向派生(和非抽象)@ 的 Item 指针(或引用) 987654323@班级。

但是因为 Item 声明没有 getName() ,你可能需要做一个 dynamic_cast 来检查指针是否真的是 Product,例如。

Item* item = new Product();
...

// at same later time or some ooter function:
// use dynamic_cast to check if the pointee is the type you expect it to be

Product* p = dynamic_cast<Product>(item);
if (p) p->getName();

虽然,不直接针对您的问题,我认为这里还有一个设计问题。像 Product 这样的东西由许多项目组成,所以它是 1:N 的组合关系。对于获得总价格的概念(例如 Billable )将是更合适的。 所以我可能会建议类层次结构如下:

class Billable
{
public:
    virtual double getTotalPrice() const = 0;
    virtual ~Billable() {}
};

class Item : public Billable
{
    string name;
public:
    string getName() const { return name; }
    double getTotalPrice() const override { ... impl .. }
    // ... rest 
}

class Product : public Billable
{
    string name;
    vector<Items> items;
public:
    string getName() const { return name; }
    double getTotalPrice() const override
    {
         return accumulate(begin(items), end(items), 0, [](auto i) { return i->getTotalPrice(); });
    }
    // ... rest ...
    
}

【讨论】:

  • 是的,这种层次结构肯定要好得多,但我想有时你只需要服从教授的意愿。
  • 明白。只是一个想法
【解决方案3】:

如果您确定指针指向Product 类型的实际对象,则可以进行静态转换;如果您不确定并想先检查它,则可以进行动态转换。如果指针不指向派生类的对象,这将不起作用。

class Item {
    public:
        virtual double getTotalPrice() = 0;
};

class Product : public Item {
    protected:
        string name;
    public:
        Product() {}
        string getName() {return {};}
        double getTotalPrice() {return {};}
};

int main() {
    Product p;
    Item *i = &p;
    
    static_cast<Product *>(i)->getName();

    auto *ptr = dynamic_cast<Product *>(i);
    if (ptr) ptr->getName();
}

dynamic_cast 需要运行时类型信息 (RTTP) 并附带一些额外费用。

通常最好重新设计架构而不是沮丧。

【讨论】:

  • 老实说,我使用了动态转换,但动态转换的问题是每次创建新的 Item 实例时都必须这样做。
  • @MericOzcan 它对我有用:wandbox.org/permlink/9hKiA4M7Q4PoNUR1
  • @MericOzcan 该问题和所有其他答案都没有包含和命名空间,但您坚持将其添加到此答案中。为什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-16
  • 2020-01-19
  • 1970-01-01
  • 2016-12-12
  • 2010-09-20
相关资源
最近更新 更多