gpt4 book ai didi

c++ - 寻找一种模式来避免在虚函数的实现中使用 dynamic_cast

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:10:52 24 4
gpt4 key购买 nike

我的问题是:我有一个带有几个具体分支类的接口(interface)根类。在应用程序代码中,存在指向根类的指针 vector 。有几个地方我需要遍历 vector 中的所有元素并将它们与给定实例进行比较:

// Application Code
void compare_loop(Root *r, std::vector<Root*> vec) {
for (auto v : vec) {
if (r->compare(v)) {
// Do something to v
}
}
}

我最初的做法是让“比较”成为 Root 类中的虚函数:

// Class Definition
class Root {
public:
Root(double bar) : Bar(bar) {};

virtual bool compare(Root *r) = 0;

protected:
double Bar;
};

class BranchA : public Root {
public:
BranchA(double bar, double baz) : Root(bar), BazA(baz) {};

bool compare(Root *r) override;

protected:
double BazA;
};

class BranchB : public Root {
public:
BranchB(double bar, int baz, bool foo) : Root(bar), BazB(baz), Foo(foo) {};

bool compare(Root *r) override;

protected:
int BazB;
bool Foo;
};

问题在于,如果参数不是同一具体类型,“比较”函数的实现应始终评估为假,否则取决于特定于 BranchA/BranchB 的成员变量。对于单个虚拟成员函数,我能想到的实现它的唯一方法是尝试 d​​ynamic_cast:

// Implementation
bool BranchA::compare(Root *r) {
BranchA* branch = dynamic_cast<BranchA*>(r);

if (branch == nullptr) {
return false;
}
else {
// Do some complicated comparison based on BranchA members
return (Bar < branch->Bar) && (BazA < branch->BazA);
}
}

bool BranchB::compare(Root *r) {
BranchB* branch = dynamic_cast<BranchB*>(r);

if (branch == nullptr) {
return false;
}
else {
// Do some complicated comparison based on BranchB members
return (Bar > branch->Bar) && (BazB > branch->BazB) && (Foo == branch->Foo);
}
}

这对我来说似乎不是最优雅的方法,但我不确定。我想知道在类定义和实现中是否可以采用不同的方法来产生相同的结果而无需更改应用程序代码。或者,这是适合使用 dynamic_cast 的实例吗?

最佳答案

我通常使用一种模式,该模式利用了 NVI 习语1,2。仍然需要强制转换,但它是 static_cast 而不是 dynamic_caststatic_cast 避免了与 dynamic_cast 相关的额外成本,并保证安全(参见代码注释)。

但是,我并不是说这个解决方案比 OP 的代码快,因为它仍然使用 typeid 检查以及 isEqual 的动态调度功能。

与问题中的代码相比,这里的主要优点是基类比较逻辑的更改不会影响派生类的实现,其中可能有很多。

示例代码

#include <iostream>
#include <memory>
#include <vector>

class Root
{
public:
explicit Root(double bar) : Bar(bar) {}

// Base class must have a virtual destructor for deletion through
// the base pointer to work properly
virtual ~Root() {}

bool operator==(const Root& other) const
{
// Make sure the two types being compared are the same derived type
return (typeid(*this) == typeid(other)) &&
// Compare all state associated with the base class
(Bar == other.Bar) &&
// Dispatch comparison to the derived implementation to finish
// the comparison
isEqual(other);
}

private:
// Guaranteed to only be dispatched by operator== if 'other' is the
// same type as '*this'
virtual bool isEqual(const Root &other) const = 0;

double Bar;
};

class BranchA : public Root
{
public:
BranchA(double bar, double baz) : Root(bar), BazA(baz) {}

private:
virtual bool isEqual(const Root& other) const override
{
// static_cast is safe since the Base class guarantees it won't
// call this function unless 'other' and '*this' are the same type
const BranchA& branch = static_cast<const BranchA&>(other);
return (BazA == branch.BazA);
}

double BazA;
};

class BranchB : public Root
{
public:
BranchB(double bar, int baz, bool foo) : Root(bar), BazB(baz), Foo(foo) {}

private:
virtual bool isEqual(const Root& other) const override
{
// static_cast is safe since the Base class guarantees it won't
// call this function unless 'other' and '*this' are the same type
const BranchB& branch = static_cast<const BranchB&>(other);
return (BazB == branch.BazB) && (Foo == branch.Foo);
}

int BazB;
bool Foo;
};

void compare_loop(const Root &r, const std::vector<std::unique_ptr<Root>>& vec)
{
for (const auto& v : vec)
{
if (r == *v)
{
std::cout << "Equivalent\n";
}
else
{
std::cout << "Different\n";
}
}
}

int main()
{
BranchA root(1.0, 2.0);

std::vector<std::unique_ptr<Root>> branches;
branches.push_back(std::make_unique<BranchA>(root));
branches.push_back(std::make_unique<BranchA>(1.0, 1.0));
branches.push_back(std::make_unique<BranchB>(1.0, 2.0, true));

compare_loop(root, branches);

return 0;
}

示例输出

Equivalent
Different
Different

Live Example


1 Non-virtual interface pattern - Wikipedia
2 Virtuality - Herb Sutter

关于c++ - 寻找一种模式来避免在虚函数的实现中使用 dynamic_cast,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38285388/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com