我的问题是合并两种技术:
假设一个具有递归函数(foo)的根类和一个覆盖此函数的扩展类(foo):override函数必须调用super :: foo,但需要在递归调用之前执行其他操作。 / p>
我将尝试一个例子(这只是一个例子,我知道有非递归方式来解决这个问题)
class Node
{
public:
// must be override
virtual int getNumValues()
{
if (parent) return parent->getNumValues() + 3;
else return 3;
}
protected:
Node *parent;
private:
int values[3];
};
class ExtNode: Node
{
public:
//@override
virtual int getNumValues()
{
int aux = Node::getNumValues(); //but need to avoid recursion here.
if (parent) return parent->getNumValues() + aux + 2;
else return aux + 2;
}
private:
int extValues[2];
};
所以我想要的是:
我正在尝试一些想法,但它们似乎很糟糕的编程实践或不可能:
// In Node class
...
virtual int getNumValues()
{
if (parent && !isNodeObject(this)) return parent->getNumValues()+3;
else return 3;
}
bool isNodeObject( Node *ob)
{
//return if ob is instance of Node (and not an extended class). How?
}
我也尝试过使用可选参数:
// In Node class
...
virtual int getNumValues( bool recursion = true)
{
if (parent && recursion) return parent->getNumValues()+3;
else return 3;
}
// In ExtNode class
...
virtual int getNumValues( bool recursion = true)
{
int aux = Node::getNumValues(false );
if (parent && recursion) return parent->getNumValues() + aux + 2;
else return aux + 2;
}
最佳编程实践是什么?
编辑1:解释我想解决的真正问题(来自Joachim Pileborg)
我正在创建一个用户界面库,即一组类和函数,用于创建容易的小部件,如框架,按钮,输入文本等。
我创建了一个具有大多数常规功能的基本(根类)小部件,一个“可见”小部件,用于实现具有可见部分的小部件的所有通用功能,以及soo。
还有一些容器,如框架,布局和窗口。
现在遇到困难的部分:有一个函数“updateStyle”应该立即更新窗口小部件的所有图形部分(并重绘它):这个函数递归调用超类来执行更多通用功能,并且还必须递归调用容器来传播更改(小部件的尺寸和位置可能会发生变化)
每个小部件都应该“按此”工作,也可以扩展,这就是为什么这些要求。
代码很广泛(大约8k行)并且有很多其他功能,所以没有必要在这里复制 代码。
答案 0 :(得分:2)
看起来您正在搜索模板方法模式:
在派生类中,覆盖特殊行为
class Node
{
public:
int getAllNumValues()
{
int allNumValues = getNumValues();
if (parent) allNumValues += parent->getAllNumValues();
return allNumValues;
}
protected:
virtual int getNumValues() {
return 3;
};
private:
Node *parent;
int values[3];
};
class ExtNode: Node
{
protected:
//@override
virtual int getNumValues()
{
return 2 + Node::getNumValues(); //but need to avoid recursion here.
}
private:
int extValues[2];
};
如果您有更新功能,我建议使用模板方法update
来执行复合模式的递归更新,另一种方法updateThis
只更新单个对象。
答案 1 :(得分:1)
根据Herb Sutter's article: Virtuality,我们应该更喜欢将虚拟功能设为私有。这意味着我们应该尽量避免调用超级"版本,而是让基类做的工作。
以下是一个例子:
class Node
{
public:
int getNumValues()
{
int result = 3 + DoGetNumValues();
if (parent)
result += parent->getNumValues();
return result;
}
private:
Node *parent;
int values[3];
virtual int DoGetNumValues() {return 0;}
};
class ExtNode : public Node
{
private:
int extValues[2];
int DoGetNumValues() override sealed {return 2 + GetMoreValues();}
virtual int GetMoreValues() {return 0;}
};
class Derived : public ExtNode
{
int GetMoreValues() override {return 1;}
};
答案 2 :(得分:0)
对于第一个例子,
Node::getNumValues()
计算树的某些功能。
ExtNode::getNumValues()
计算树的另一个函数。
ExtNode::getNumValues()
是( Node::getNumValues(), tree )
的函数,或者仅取决于树。
对于UI问题, 想想责任链设计模式。将更新请求转发到根节点,根节点又启动树遍历以从root开始更新所有节点。
答案 3 :(得分:-1)
解决这个问题的一种方法是使函数非虚拟,然后在每个覆盖中显式调用super classe函数(类似于构造函数)。
使方法非虚拟意味着每个继承的类都将拥有它自己的方法实现,因此您不会通过编写实现来覆盖classe的父代码。功能。
缺点是您必须通过明确指定某种类型的指针调用该函数,从而迫使您知道该类型。
要避免这个缺点,请创建一个调用所需递归函数的虚函数,然后使用该函数。
作为旁注,应避免使用非虚函数。
这是一个示例代码
class base
{
public:
int doStuff()
{
printf(" base called ");
return 0;
}
};
class ext : public base
{
public:
int doStuff()
{
base::doStuff();
printf(" ext called ");
return 0;
};
};
class ext2 : public ext
{
public:
int doStuff()
{
ext::doStuff();
printf(" ext 2 called");
return 0;
};
};
void runTest()
{
base* ptr = new ext2();
ptr->doStuff();
ext2* recast = (ext2*) ptr;
recast->doStuff();
}
对于上面的代码,输出将是"名为ext的名为ext的base称为ext2,名为"。
如果在基类中声明doStuff函数是虚拟的(从而使其对每个子类都是虚拟的),那么输出将是"名为ext2的名为ext2的名为base的名为ext的名为ext2的名为"。