运算符代码继承的 C++ 问题:我是不是需要为所有派生类复制相同的代码?
Posted
技术标签:
【中文标题】运算符代码继承的 C++ 问题:我是不是需要为所有派生类复制相同的代码?【英文标题】:C++ Trouble with operator code inheritage: am I require to copy same code for all derived classes?运算符代码继承的 C++ 问题:我是否需要为所有派生类复制相同的代码? 【发布时间】:2010-01-13 16:23:36 【问题描述】:我想编写一组处理一些(例如布尔值)操作的 D_I 类(又名 I=1,2,...只是我的意思是 '_I' 是类专有名称,而不是整数) F 对于所有类具有相同的含义。但我也想对这些类的对象的“总和”进行操作,组的不同类可能被“添加”在一起。上述对象求和的操作取决于相加对象的相应操作。
这就是为什么我要创建通用的基类 B 来处理“求和”的操作:
class B
public:
B (): LEFT(NULL), RIGHT(NULL) ;
B (const SpBrd& S);
B (const B& A, const B B);
~B ();
if (LEFT != NULL) delete LEFT; // NULL if it is default B or D_I
if (RIGHT != NULL) delete RIGHT; // NULL if it is default B or D_I
B operator + (const B& S) const;
bool F(const Point& P) const;
bool b = aF(P);
if (LEFT != NULL) b = b && LEFT->F(P); // this is how F depends
if (RIGHT != NULL) b = b && RIGHT->F(P); // of added classes
protected:
virtual bool aF(const Point& P) const return true;; // Default!
B* LEFT; // Pointer to left operand of "sum"
B* RIGHT; // Pointer to right operand of "sum"
// (since it might point to D_i class too as well)
;
所以这种方式派生类 D 很容易编写,因为它应该只处理它的构造函数和 aF:
class D_I: public B
public:
D() ...;
protected:
virtual bool aF (const Point& P) const
// return something here
;
问题是:如何编写运算符+和B类的复制构造函数:
B:B (const B& S)
if (S.LEFT != NULL) LEFT = new B (S.LEFT)
if (S.RIGHT != NULL) RIGHT = new B (S.RIGHT)
;
B B:operator + (const B& S) const
SpBrd S;
S.LEFT = new B (*this);
S.RIGHT = new B (S);
;
不会产生正确的结果,因为如果 'operator +' 或 'S.LEFT' 中的 'this'、'S' 或复制构造函数中的“S.RIGHT”并不是指 B 类的对象,而是指 D_I 类之一的对象。
所以,我找不到让 B::operator+ 知道 sum 的类型来源的方法,因为可能存在大量 D_I 并且会不时添加它们。我应该怎么做才能正确地写这一切?
我不能为所有 (D_I,D_J) 对编写 D_I operator+ (D_J) 和 D_I::D_I(const D_J) 的 pars,这是不公平的!
(这将表示空间中的边界(表面)。函数 F 表示在边界内 - 即检查该点是否在空间主体内。但是我想要操作的边界内的物体可能是交集( operator+) 在其他两个边界内的空间体。示例:两个球体边界的交集——内部和外部区域——产生球壳,也就是 3D 环;可能还想查看半径为 R 和立体角为 10*10 的交点球面边界围绕极点的度数等等。)
【问题讨论】:
请原谅 *** 不尊重我的段落中断,但无论如何:SpBrd
是什么?底部的 Operator+ 没有语法意义。 class B
甚至不会在我的脑海中编译,因为它的 ctors 和 dtor 的名称是错误的。另外,考虑改写你的问题。不清楚:“团体课”?避免被动语态。无论我如何尝试阅读它,“1) in operator + if *this and S are not B but one D_I should be 'new D_I”对我来说甚至不像一个正确的英文句子。第 2 点同上)。我会停在这里。
哦,我的上帝,我很抱歉......问这个问题我只是通过删除额外的函数来删除我的程序片段,这里无关紧要,并将 SpBrd 和 SphBrd 等类名更改为 Base 和 Derive (简称 B 和 D)以免使读者感到困惑,但事实恰恰相反……我还对那段进行了释义。再次抱歉。
我更新了我的答案。我认为它回答了您现在提出的问题,尽管我仍然对此感到困惑。
您是说要为每个 i 定义一个类 D_i 吗?在一个名为 D_1 的类中,另一个名为 D_2,第三个名为 D_3 等等?如果是这样,请改用模板,并定义一个类 template <int i> class D;
在您接受的答案中,我似乎重新发明了 Curiously recurring 模板模式,因为它适用于复制构造。见:en.wikipedia.org/wiki/…
【参考方案1】:
你的问题令人困惑,我觉得这里很匆忙,但我会说这个。
通常,运算符在类外部声明为非成员函数,并使用成员函数来实现其目标。这样操作符的两个参数就可以平等地参与重载决议。
在这种情况下,您似乎正在尝试创建某种基于 C++ 运算符的解析树。在这种情况下,您可能正在寻找这样的代码:
const B operator +(const B &a, const B &b)
return B(a, b);
请注意,即使从 B 派生的东西是任一操作数,这也将起作用。如果我对你所做的事情是正确的,你可能会有一个特殊的节点类型用于两个相互添加的东西,并且 operator + 可以返回它。
现在我看到了您的澄清版本,这是一个不同的答案。
首先,我质疑您以这种方式使用运算符 +。运算符重载通常会给使用你的类的人带来惊喜,除非你使用 operator + 的行为与人们期望 operator + 的行为非常相似,否则它会导致它解决的更多问题。
但您的实际问题似乎围绕着创建未知类型对象的副本而没有大量重复代码。当我看到重复的代码时,我倾向于想到模板。下面是一些示例代码,可能比您需要的使用模板更复杂一些,我认为可以解决您的问题。
#include <memory>
#include <iostream>
template <class BaseType, class DerivedType>
class Cloneable
public:
virtual ~Cloneable()
::std::auto_ptr<DerivedType> clone() const
return ::std::auto_ptr<DerivedType>(static_cast<DerivedType *>(i_clone()));
protected:
virtual BaseType *i_clone() const
return new DerivedType(dynamic_cast<const DerivedType &>(*this));
;
class A : public Cloneable<A, A>
public:
A()
A(const A &b)
const void * const voidb = &b;
const void * const voidme = this;
::std::cerr << "Creating a copy of the A at " << voidb << " and this new copy will reside at " << voidme << "\n";
;
virtual ~A()
const void * const voidme = this;
::std::cerr << "Destroying the A at " << voidme << "\n";
;
template <class Derived>
class B : public A, public Cloneable<A, Derived>
public:
B()
B(const B &b)
const void * const voidb = &b;
const void * const voidme = this;
::std::cerr << "Creating a copy of the B at " << voidb << " and this new copy will reside at " << voidme << "\n";
;
virtual ~B()
const void * const voidme = this;
::std::cerr << "Destroying the B at " << voidme << "\n";
// Make sure clone can be mentioned in derived classes with no ambiguity
using Cloneable<A, Derived>::clone;
protected:
// Force dominance rules to choose the correct i_clone virtual function.
virtual A *i_clone() const
return Cloneable<A, Derived>::i_clone();
;
class C : public B<C>
public:
C()
C(const C &b)
const void * const voidb = &b;
const void * const voidme = this;
::std::cerr << "Creating a copy of the C at " << voidb << " and this new copy will reside at " << voidme << "\n";
;
virtual ~C()
const void * const voidme = this;
::std::cerr << "Destroying the C at " << voidme << "\n";
;
class D : public B<D>
public:
D()
D(const D &b)
const void * const voidb = &b;
const void * const voidme = this;
::std::cerr << "Creating a copy of the D at " << voidb << " and this new copy will reside at " << voidme << "\n";
;
virtual ~D()
const void * const voidme = this;
::std::cerr << "Destroying the D at " << voidme << "\n";
;
int main(int argc, const char *argv[])
C c;
D d;
::std::auto_ptr<A> cptr(c.clone());
::std::auto_ptr<A> dptr(d.clone());
cptr = dptr->clone();
return 0;
我创建了 i_clone 和 clone 方法,这样每个类最终都会得到一个 clone 版本,它返回一个指向该类自己类型的指针。我还必须在模板类 B 中有 using
声明,以确保在派生类中调用 clone 没有歧义。
注意 C 类和 D 类如何不包含与创建它们自己的克隆相关的重复代码。
虽然当时 a 并不知道,但这似乎是对 The Curiously Recurring Template 习语的又一次重新发明,应用于 polymorphic copy construction。
【讨论】:
返回const B
有什么意义?我已经看过很多次了,但我看不出重点,因为我可以完美地写出B result = a + b;
。我在这里错过了什么吗?
我总是从函数中返回 const X 以避免允许像 (a + b) = 5;
这样的奇怪结构。
@Matthieu:它使a + b = c
之类的表达式无效,因为它们具有内置类型。当取消引用运算符返回副本时,这更是一个问题 - 你希望 a[i] = b
编译失败,而不是静默分配给临时对象。
哦,当然,现在我明白了:每个 D_I 都有自己的父 B要准确地遵循您想要做的事情(SpBrd 是什么?)有点困难,但您可以考虑制作一个像这样的新虚函数:
class B
...
virtual B *Clone() const return new B(*this);
...
;
class D: public B
...
virtual D *Clone() const return new D(*this);
...
;
然后可以这样使用:
S.LEFT = this->Clone();
S.RIGHT = S.Clone();
【讨论】:
非常感谢,这肯定可以正常工作。唯一的缺点是我应该用几乎相同的代码为所有 D_I 类创建这样的复制函数!如果 C++ 让使用不是“new Type (arg)”而是“pointer_to_object_of_Type -> operator new (arg)”,那就太好了,但我不知道它是否可能。再次感谢并为 SpBrd 感到抱歉,当然是 B。而 D_I 将是 SphBrd、AlphaBrd、DeltaBrd 等等...... 这些虚函数应该是 const 限定的,即 virtual B *Copy() const 这种模式通常使用动词“克隆”而不是“复制”来指代——命名你的函数 Clone() 可能会更清楚地传达意图。参见例如Java Cloneable 接口:java.sun.com/j2se/1.4.2/docs/api/java/lang/Cloneable.html 另外,您可能希望D::Clone
返回D*
。这称为协变返回类型并且有效,因为 D*
可以转换为 B*
。不过,它的好处在于,如果您使用的是 D* obj = new D();
,那么您可以编写 D* newObject = obj->Clone();
,从而充分利用您所拥有的类型信息。
我找到了一种让模板为您生成克隆函数的方法。 :-)以上是关于运算符代码继承的 C++ 问题:我是不是需要为所有派生类复制相同的代码?的主要内容,如果未能解决你的问题,请参考以下文章