C ++抽象基类,具有纯虚拟运算符+函数

时间:2013-12-29 23:31:10

标签: c++ inheritance interface operator-overloading abstract-class

假设我正在创建一个抽象基类来定义公共接口。我希望operator +成为这个公共接口的一部分。似乎这在C ++中是不可能的,或者我错过了一些东西。

例如,假设下面的代码是.h文件,声明了名为IAbstract的抽象基类的公共接口。此代码无法编译,因为我无法声明一个返回抽象基类实例的函数。这个编译错误是有道理的,但我不明白的是:有没有办法保证运算符+将存在于IAbstract的公共接口中?

#include <iostream>

// An abstract base class
class IAbstract
{
public:
    IAbstract(int value);
    virtual ~IAbstract() {}
    virtual int SomeOtherFunction() =0;

    //This is ok
    virtual IAbstract& operator++() =0;

    //This will not compile
    virtual const IAbstract operator+(const IAbstract& rhs) =0;
    //Xcode5 says "Semantic Issue: Return type 'IAbstract' is an abstract class"
};

5 个答案:

答案 0 :(得分:2)

除了按值返回问题之外,你的dessign真正的问题是 C ++语言的工作方式

Java等面向对象语言通过类层次结构,接口和多态定义和传输功能。您的代码就是典型的例子。
C ++不能以这种方式工作。 C ++通过抽象概念定义功能,而类只需要实现这些概念以便以某种方式使用。如何/如果一个类遵循或实现某个概念取决于它的行为,某些函数的实现(如重载运算符)等。

因此,C ++实现您要实现的目标的方法是以通用方式重载所需的运算符,以确保实现该概念的所有类都能使用它
换句话说,如果您尝试创建一个可添加的概念,即表示可以与其他人一起添加的内容,则类只需要重载operator+。如果一个类重载operator+它的可添加项。简单。

这是另一个例子:std::copy()的经典实现:

template<typename IT , typename DEST_IT>
void copy( IT begin , IT end , DEST_IT dest_begin )
{
    while( begin != end )
        *dest_begin++ = *begin++;
}

在该实施中,beginend的类型是什么? 简单回答:我们不知道。可能是任何东西。但可能是必须满足我们的功能的任何要求,即:是不可分辨的,可递增的和可比较的。换句话说:它的 可迭代 类型。一种工作(似乎是)迭代器的类型。

因此std::copy()适用于可迭代事物所代表的任何范围。可以是数组:

int main()
{
    int a[] = { 1,2,3 };
    int b[3];

    int* begin_a = &a[0];
    int* end_a   = &a[2];
    int* begin_b = &b[0];

    //Pointers are an iterable thing, right?:
    std::copy( begin_a , end_a , begin_b );

    //Or just the common generic way (Which does exactly the same):
    std::copy( std::begin( a ) , std::end( a ) , std::begin( a ) );
}

向量,列表等:

int main()
{
    std::vector<int> a = { 1,2,3 };
    std::vector<int> b;

    //Wooh, the code is exactly the same! Thats why templates are cool.
    std::copy( std::begin( a ) , std::end( a ) , std::begin( a ) );
}

更多令人讨厌的东西,比如遍历流的迭代器:

int main()
{
    std::vector<int> a = { 1,2,3 };

    //Copy a vector to the standard output? Yes we can!
    std::copy( std::begin( a ) , std::end( a ) , std::ostream_iterator<int>( std::cout , "\n" ) );
}
  

1
  2
  3

你自己的课程:

struct numeric_iterator
{
    int value;

    numeric_iterator( int initial_value = 0 ) : value( initial_value ) {}

    //numeric_iterator is an iterable thing because...

    //... its dereferenciable
    int operator*() const
    {
        return value;
    }

    //... its incrementable
    numeric_iterator& operator++()
    {
        ++value;
        return *this;
    }

    numeric_iterator operator++(int)
    {
        numeric_iterator copy( *this );

        ++(*this);

        return copy;
    }

    //... and its comparable
    friend bool operator==( const numeric_iterator& lhs , const numeric_iterator& lhs )
    {
        return lhs.value == rhs.value;
    }

    friend bool operator!=( const numeric_iterator& lhs , const numeric_iterator& lhs )
    {
        return !( lhs == rhs );
    }
};

int main()
{
    //Tah dah!
    std::copy( numeric_iterator( -4 ) , 
               numeric_iterator(  5 ) , 
               std::ostream_iterator<int>( std::cout , " " ) 
             );
}
  

-4 -3 -2 -1 0 1 2 3 4

答案 1 :(得分:1)

违规行是:

virtual const IAbstract operator+(const IAbstract& rhs) =0;

该方法按值返回,因此复制并创建IAbstract类型的对象,这是不允许的。你应该改为:

virtual const IAbstract& operator+(const IAbstract& rhs) =0;

这似乎是你的意图,但很容易疏忽。

答案 2 :(得分:0)

你所拥有的地方:IAbstract variable,你实际上永远不会有IAbstract,只能指向它(或引用)。

因为某个地方你尝试处理IAbstract而烦恼。

它希望IAbstract*IAbstract&IAbstract&&都很好(以某种形式(const和volatile,等等))。

你去了,你从操作员++返回IAbstract,这没有任何意义,因为你可以从不拥有IAbstract,只需将其视为一个。< / p>

答案 3 :(得分:0)

不,保证operator +不存在于IAbstract派生类的公共接口中是不可能的,因为你无法正确地将operator +声明为纯虚函数。

答案 4 :(得分:0)

诀窍是将抽象基类封装在常规类型中。解决方案要求有一个克隆方法

var array =  [{"heure1": "14:00","heure2": "17:00","day": "Sunday",}, {"heure1": "08:00","heure2": "13:00","day": "Sunday",}, {"heure1": "14:00","heure2": "16:00","day": "Monday",}, {"heure1": "08:00","heure2": "18:00","day": "Monday", },];

let days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
let obj = Object.fromEntries(days.map(day => [day, { heure: [], day }]));
for (let {heure1, heure2, day} of array) obj[day].heure.push(heure1, heure2);
let result = Object.values(obj);

console.log(result);
class AbstractNumericType
{
public:
    virtual AbstractNumericType& operator+=(AbstractNumericType const& other) = 0;
    virtual std::unique_ptr<AbstractNumeric> clone() = 0;
    virtual ~AbstractNumericType() = default;
};

演示:https://gcc.godbolt.org/z/e4v1Tr

这里的真正问题是在没有可用的具体类型时如何实现class NumericType { public: template<class Something> NumericType(Something value):m_obj{createNumber(value)} {} NumericType(NumericType const& other): m_obj{other.m_obj->clone()} {} NumericType operator+(NumericType const& other) const { auto ret = *this; (*ret.m_obj) += (*other.m_obj); return ret; } private: std::unique_ptr<AbstractNumericType> m_obj; }; 。您可以添加多个重载(如访问者模式),也可以使用基于rtti的类型开关。这些解决方案都不是真的好。