【问题标题】:C++ operator overloading with inheritance带有继承的 C++ 运算符重载
【发布时间】:2013-06-15 09:25:59
【问题描述】:

假设我有一个名为 Vehicle 的类和另一个名为 Car 的类,它扩展了 Vehicle 类。我想为这两个类实现++ 运算符。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <sstream>
#include <typeinfo>

#define debug(args...) // Just strip off all debug tokens
using namespace std;
// CUT begin
#define debug(args...) {dbg,args;cout<<endl;}
struct debugger{template<typename T> debugger& operator ,(const T& v){cout<<v<<" ";return *this;}}dbg;
template <typename T1,typename T2> inline ostream& operator<<(ostream& os,const pair<T1,T2>& p){return os<<"("<<p.first<<", "<<p.second<<")";}
template<typename T>inline ostream&operator<<(ostream& os,const vector<T>& v){string delim="[";for(unsigned int i=0;i < v.size();i++){os<<delim<<v[i];delim=", ";}return os<<"]";}
template<typename T>inline ostream&operator<<(ostream& os,const set<T>& v){string delim="[";for (typename set<T>::const_iterator ii=v.begin();ii!=v.end();++ii){os<<delim<<*ii;delim=", ";}return os<<"]";}
template<typename T1,typename T2>inline ostream&operator<<(ostream& os,const map<T1,T2>& v){string delim="[";for (typename map<T1,T2>::const_iterator ii=v.begin();ii!=v.end();++ii){os<<delim<<*ii;delim=", ";}return os<<"]";}
// CUT end


class Vehicle
{
public:
    int n;
    Vehicle(int n):n(n){cout<<"Ctor Vehicle "<<n<<endl;}
    Vehicle(Vehicle& v):n(v.n){cout<<"Copy Ctor Vehicle "<<n<<endl;}
    virtual ~Vehicle(){cout<<"Dtor Vehicle "<<n<<endl;}
    virtual ostream& dump(ostream& os){return os<<"Vehicle("<<n<<")";}
    string to_str(){stringstream s; dump(s); return s.str();}
    virtual Vehicle& operator++(){n++;return *this;}
    virtual Vehicle operator++(int x){Vehicle v(*this); operator++(); return v;}
};

class Car: public Vehicle
{
public:
    Car(int n): Vehicle(n){cout<<"Ctor Car "<<n<<endl;}
    virtual ~Car(){cout<<"Dtor Car "<<n<<endl;}
    virtual ostream& dump(ostream& os){return os<<"Car("<<n<<")";}
    virtual Car operator++(int x){Car v(*this); operator++(); return v;}
    /* data */
};
ostream& operator<<(ostream& os,  Vehicle& v)
{
    return v.dump(os);
}

int main(int argc, char const *argv[])
{
    Vehicle * v = new Car(10);
    // cout<<c++<<endl;
    // cout<<c<<endl;
    return 0;
}

我收到以下 gcc 错误:

C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:16:0: warning: "debug" redefined [enabled by default]
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:13:0: note: this is the location of the previous definition
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:44:14: error: invalid covariant return type for 'virtual Car Car::operator++(int)'
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:35:18: error:   overriding 'virtual Vehicle Vehicle::operator++(int)'
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp: In member function 'virtual Car Car::operator++(int)':
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:44:57: error: no matching function for call to 'Car::operator++()'
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:44:57: note: candidate is:
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:44:14: note: virtual Car Car::operator++(int)
C:\Users\Rajat\Documents\GitHub\interview-preparation\cpp_test.cpp:44:14: note:   candidate expects 1 argument, 0 provided

我如何获得CarVehicle++ 运算符,并且虚拟覆盖的数量最少?

【问题讨论】:

  • 你试过定义virtual Car&amp; operator++()并编译吗?

标签: c++ inheritance operator-overloading


【解决方案1】:

改变

virtual Car operator++(int x){Car v(*this); operator++(); return v;}

virtual Vehicle operator++(int x){Car v(*this); Vehicle::operator++(); return v;}
  1. 在覆盖 operator++ 时,不应更改返回类型。
  2. 明确提到要调用父类的操作符++Vehicle::operator++()

通过该更改,您的程序会生成此输出

Ctor Vehicle 10
Ctor Car 10

【讨论】:

  • operator++(int) 对应于x++operator++() 对应于++x。我两个都需要。
  • -1 有点不必要。 @thefourtheye 还是给出了基本的解决方案。
  • 算子不能是虚拟的
【解决方案2】:

另一种方法是通过CRTP 和运算符重载助手(如boost operators header

假设你有以下助手:

template<typename T>
struct AddHelper
{
    T& operator++()
    {
        T& reference = static_cast<T&>(*this);
        reference.add();
        return reference;
    }

    T operator++(int)
    {
        AddHelper<T> copy( *this );
        operator++();
        return static_cast<T>(copy);
    }
};

add() 实现由基类提供:

class Vehicle
{
private:
    int _n;
public:
    void add(int n) { _n += n; }

    ...
};

因为Vehicle::add() 是公开的,我们可以在每个 Vehicle 子类中使用它,这意味着您可以为每个 Vehicle 子类拥有特定的 operator++,这要归功于 AddHelper:

class Car : public Vehicle , public AddHelper<Car>
{
    Car(int n) : Vehicle(n) {}
    ...
};

class Motorcicle : public Vehicle , public AddHelper<Motorcicle>
{
    Motorcicle(int n) : Vehicle(n) {}
    ...
};

class Bus : public Vehicle , public AddHelper<Bus>
{
    Bus(int n) : Vehicle(n) {}
    ...
};

... //Ad infinitum

这种方式的另一个优点是它不使用虚函数来提供多态性,因此效率更高(静态多态性而不是动态多态性)。

【讨论】:

  • Car c(10); c++; c 将如何确定要调用哪个 operator++(int)AddHelper&lt;Car&gt;::operator++AddHelper&lt;Vehicle&gt;::operator++?
  • @prongs Car 继承自 AddHelper&lt;Car&gt;,因此在该模板实例中,T 是 Car。这意味着如果您调用 Car::operator++(),您将调用 AddHelper::operator++()。而且,例如,如果您调用 Bus::operator++(),那么您实际上是在调用 AddHelper::operator++()。
  • @prongs 诀窍是因为您从模板继承,所以该模板是特定于您的类的。换句话说,编译器为每个继承自 AddHelper 的类生成一个 operator++
  • @pongo 哦,现在我明白了。这种类型的解决方案在设计时考虑了抽象基类(您永远不会直接实例化 Vehicle,而是实例化一个子类)。所以基类(The Vehicle),不是继承自 AddHelper。
  • 不,我了解模板,这是一项非常出色的技术。我的问题是,Car 直接从 AddHelper&lt;Car&gt;AddHelper&lt;Vehicle&gt; 间接继承(一级间接)。所以基本上声明c++ 要求它搜索两个类中都存在的operator++。如何挑选一个而不是另一个?
猜你喜欢
  • 2011-12-05
  • 2012-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多