当前位置: 首页 > 知识库问答 >
问题:

编译时纯虚函数

尹赞
2023-03-14

我试着实现了一个列表容器,并决定将一些通用函数如< code>sum()移到基类,这样我就可以在其他容器中重用它们。

所有的基本支持类需要的是三个方法 empty()、head()tail我不能使这些纯粹的虚拟,因为支持类永远不会被实例化。但它仍然必须使用这些方法来实现自己的方法,如 sum()。

我试过这样的东西:

#include <iostream>
using namespace std;

template<typename T>
class StatsSupport {
public:
    T sum(void) const {
        if (empty()) {
            return T(0);
        } else {
            return head() + tail()->sum;
        }
    }

    // other methods
};

template<typename T>
class List : public StatsSupport<T> {
public:
    // constructors etc.

    bool empty(void) const {return head_ != NULL;}
    const T& head(void) const {return *head_;}
    const List<T>& tail(void) const {return *tail_;}

    // other methods
private:
    T* head_;
    List<T> *tail_;
};

但是尝试使用< code>sum()会导致编译错误

prog.cpp:8:13: error: there are no arguments to 'empty' that depend on a template parameter, so a declaration of 'empty' must be available [-fpermissive]
   if (empty()) {
             ^

对于空()head()尾()中的每一个。

有什么建议吗?

共有3个答案

苏硕
2023-03-14

我可能会错过问题的重点,但无论如何都会给我5美分:)

我下面展示的解决方案背后的理由是,通常新接触OOP(在C中)的人认为他们必须使用继承来完成事情。

但特别是在C语言中,这只是实现合成的一种方式,通常不是最好的方式。

虽然在大多数情况下,虚拟函数的开销成本并不重要,但下面的代码显示了一种在不使用继承和不使用虚函数的情况下产生容器扩展的方法。该方法的弱点是“容器函数合约”只是隐式可见的。

template <class _X>
class ContainerTypeA < _X >
{
public:
    typedef _X value_type;
    typedef ContainerTypeA<_X> container_type;

    const _X & Head() const
    {
        // return head of this containers content.
    }
    container_type Tail() const
    {
        // return the tail (all elements after the first element in a new instance.
    }
    bool IsEmpty() const
    {
        return true; // return whether or not this container is empty.
    }
};

template <class _X>
class ContainerTypeB < _X >
{
public:
    typedef _X value_type;
    typedef ContainerTypeB<_X> container_type;

    const _X & Head() const
    {
        // return head of this containers content.
    }
    container_type Tail() const
    {
        // return the tail (all elements after the first element) in a new instance.
    }
    bool IsEmpty() const
    {
        return true; // return whether or not this container is empty.
    }
};

// Note: In stead of the class with static member functions, this could 
// as well be a namespace with template-functions inside.
template < class _ContainerT >
class ContainerStats<_ContainerT>
{
    static _ContainerT::value_type Sum(const _ContainerT & container)
    {
        // Implement sum - possibly in the recursive way you did in your question.
    }
    // more expansion functions...
};
相温文
2023-03-14

这是一个严重的设计问题:您的< code>StatSupport定义了一些通用函数,但是依赖于它的子类的细节。

所以当 StatSupport 被编译时,它甚至不知道有一些 head()tail()。这就是您收到错误消息的原因

现在想象一下,有一天你想要定义从< code>StatSupport继承的其他容器,例如你自己的Vector或Map,或者数据库。这些数据结构没有头和尾。

基本上有两个主要方向:

  • 在 StatSupport 中定义一些用于迭代数据结构的虚拟函数。
  • 或者更好的是,在数据结构中使用一些迭代器(就像它们存在于标准容器中一样),并定义一些使用迭代器浏览容器的模板函数(总和、平均值等)。

在后一种情况下,您不需要继承来受益于泛型函数。

谢璞
2023-03-14

问题是 StatsSupport 找不到函数、head 等函数,因为这些函数既不存在于其范围内,也不存在于全局范围内。StatsSupport 不知道派生类中存在的函数。

基本上有两种方法可以解决这个问题:

  • 运行时多态性,您将虚拟析构函数添加到 StatsSupport,并为等添加纯虚拟声明。
  • 注释中所述,通过使用 CRTP 编译时多态性。我将着重谈后者。

所以基本上,StatsSupport需要获得访问派生类函数的方法。这可以通过将派生类的类型添加为模板参数来实现,该模板参数称为CRTP:

template<class Derived, typename T>
class StatsSupport {
public:
    T sum(void) const {
        if (derived()->empty()) {
            return T(0);
        } else {
            return derived()->head() + derived()->tail()->sum;
        }
    }

    // other methods
    private:
        Derived *derived()
        {
            return static_cast<Derived*>(this);
        }
        const Derived *derived() const
        {
            return static_cast<const Derived*>(this);
        }
};

template<typename T>
class List : public StatsSupport<List<T>, T> { // with some changes could be simplified to StatsSupport<List<T>> but this it ouf of scope of this question

我使用派生的函数而不是成员来保持类const正确。

当然,另一种选择是依赖算法的不同设计。在这里,您将< code>sum和< code>StatsSupport的所有其他函数移动到全局名称空间中,然后像< code > sum(my _ container _ instance)一样访问它们。更像STL的方法是使用迭代器。然后,您可以使用< code>std::accumulate进行求和。

 类似资料:
  • 我正在学习面向对象的C,并有一个关于虚拟/纯虚拟和多级继承的问题。 假设我有这样的简单代码: 我的理解是,除非getWidth被指定为虚拟,否则多态将使用“Base”类的函数。我的意思是r-的最终调用 在这种情况下,我注意到如果我删除Shape中的纯虚拟声明,我们会得到我刚才描述的行为。在基类中有一个纯虚函数会自动使该函数的所有定义都是虚的吗?

  • 本文向大家介绍C++中虚函数与纯虚函数的用法,包括了C++中虚函数与纯虚函数的用法的使用技巧和注意事项,需要的朋友参考一下 本文较为深入的分析了C++中虚函数与纯虚函数的用法,对于学习和掌握面向对象程序设计来说是至关重要的。具体内容如下: 首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象、继承、动态绑定。通过数据抽象,可以使类的接口与实现分离,使

  • 本文向大家介绍虚函数与纯虚函数之间的区别,包括了虚函数与纯虚函数之间的区别的使用技巧和注意事项,需要的朋友参考一下 在本文中,我们将了解虚拟和纯虚拟功能之间的区别。 虚函数 它在类中有自己的定义。 基类可以覆盖虚拟函数。 它没有派生类。 声明 纯虚函数 没有定义。 如果一个类至少具有一个虚函数,则可以将其声明为抽象。 派生类必须重写纯虚函数才能使用它。 通过在声明中放置“ = 0”来指定纯虚函数

  • 本文向大家介绍C++ 虚函数和纯虚函数的区别分析,包括了C++ 虚函数和纯虚函数的区别分析的使用技巧和注意事项,需要的朋友参考一下 首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数。 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。 定义一个函数为纯虚函数,才代表函数没有被实现。 定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函

  • 在 C++中,可以将虚函数声明为纯虚函数,语法格式为: virtual 返回值类型 函数名 (函数参数) = 0; 纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上 ,表明此函数为纯虚函数。 最后的 并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。 包含纯虚函数的类称为抽象类(Abstract Class)。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。

  • 我(希望)已经明白了私有虚函数的用途,但是还没明白为什么要有私有的纯虚函数!我的意思是,我必须在所有的派生类中定义这个函数,但是这样一来,所有对这个虚拟纯函数的调用都是对在派生类中定义的函数的调用。 假设我们有: Base的派生类必须定义bar(或使其纯虚拟,但暂时忘记这一点)。因为Base::bar是私有的,所以派生类不能使用它。因为Base是抽象的,我无法创建Base对象,因此我只能在派生类上