【问题标题】:Virtual functions with differences here and there虚函数随处可见
【发布时间】:2015-08-12 02:10:42
【问题描述】:

考虑这个输出:

Current time: 6:30 pm
Current time: 18:30
Current time: evening.
Current time: evening (for many it is dinner time, but many eat dinner later).

请注意,最后两个有句号,而前两个没有。我从下面的代码中使用System::displayCurrentTime 成员函数获得了这个所需的输出:

#include <iostream>
#include <string>
#include <memory>

class TimeDisplay {
    public:
        virtual std::string tell() const = 0;
        virtual std::string tellMaybeWithPeriod() const = 0;
};

class ClockDisplay12Hours : public TimeDisplay {  // #1
    std::string tell() const override {return "6:30 pm";}
    std::string tellMaybeWithPeriod() const override {return tell();}
};

class ClockDisplay24Hours : public TimeDisplay {  // #2
    std::string tell() const override {return "18:30";}
    std::string tellMaybeWithPeriod() const override {return tell();}
};

class DescriptiveTimeDisplay : public TimeDisplay {  // #3
    std::string tell() const override {return "evening";}
    std::string tellMaybeWithPeriod() const override {return tell() + ".";}
};

class CrazyDescriptiveTimeDisplay : public TimeDisplay {  // #4
    std::string tell() const override {return "evening (for many it is dinner time, but many eat dinner later)";}
    std::string tellMaybeWithPeriod() const override {return tell() + ".";}
};

struct System {
    static std::shared_ptr<TimeDisplay> timeDisplay;
    static std::string timeAsString() {return timeDisplay->tell();}
    static std::string timeAsStringMaybeWithPeriod() {return timeDisplay->tellMaybeWithPeriod();}
    // #3 and #4 will have a period, the others will not.
    static void displayCurrentTime (std::shared_ptr<TimeDisplay> t) {
        timeDisplay = t;
        std::cout << "Current time: " << System::timeAsStringMaybeWithPeriod() << '\n';
    }
    static void foo (std::shared_ptr<TimeDisplay>) {}  // #1 and #3 will have a period, the others will not.
    static void bar (std::shared_ptr<TimeDisplay>) {}  // #1, #2, and #4 will have a period, the others will not.
    static void baz (std::shared_ptr<TimeDisplay>) {}  // #2 will have a period, the others will not
};
std::shared_ptr<TimeDisplay> System::timeDisplay;

int main() {
    const std::shared_ptr<TimeDisplay> clocks[] = {std::make_shared<ClockDisplay12Hours>(), std::make_shared<ClockDisplay24Hours>(),
        std::make_shared<DescriptiveTimeDisplay>(), std::make_shared<CrazyDescriptiveTimeDisplay>()};
    for (std::shared_ptr<TimeDisplay> t : clocks)
        System::displayCurrentTime(t);
}

这不是很乱,但请注意,下一个实现foobarbaz 的函数需要TimeDisplay 的不同派生类的句点,而实际上有不止4 个这样的派生类,还有超过 3 个新的成员函数需要处理。有没有比为foobarbaz 等每个写出新的虚函数更简洁更优雅的方式来处理这些即将到来的成员函数... ?以某种方式使用模板(例如,重命名派生类Derived&lt;0&gt;Derived&lt;1&gt; 等......然后使用这些编译时整数来适应上面 cmets 中所述的规则)?或许可以避免使用模板并做其他事情?

【问题讨论】:

  • 请澄清您的问题。
  • 如何最好地实现foobarbaz 等...通过遵循上述cmets 中所述的规则,而不重复displayCurrentTime 造成的混乱?否则,新的虚函数的数量就会爆炸式增长。我将foobarbaz 留空,因为只有期间业务需要担心。
  • 使用以下代码有什么问题:static void displayCurrentTime (std::shared_ptr&lt;TimeDisplay&gt; t) { std::cout &lt;&lt; "Current time: " &lt;&lt; t-&gt;tellMaybeWithPeriod() &lt;&lt; '\n'; }
  • 因为tellMaybeWithPeriod() 仅适用于#3 和#4 获取期间。上面提到的所有其他情况呢?
  • 所以有 4 种不同的显示类型可以传递给 4 个不同函数中的任何一个,每个函数都有不同的尾随句点存在规则。是的,这显然是在创造 4x4=16 的可能性……我的第一反应不是试图解决这个设计,而是回到实际需求,看看是否有不同的方法来完全处理这个问题。 (例如,一个接受两个参数的函数:显示类型和是否有句点的布尔值。)

标签: c++ inheritance virtual


【解决方案1】:

可以通过给四个时钟显示类一个bool模板参数来完成。例如

template <bool P>
class DescriptiveTimeDisplay : public TimeDisplay {  // #3
    std::string tell() const override { return "evening"; }
    std::string tellMaybeWithPeriod() const override { return tell() + (P ? "." : ""); }
};

并通过实例化类来控制是否显示句点,例如

std::make_shared<DescriptiveTimeDisplay<true>>()

对于displayCurrentTimefoobarbaz这四个函数中的每一个,您可以通过用不同的布尔模板参数实例化四个TimeDisplay子类来控制它们的显示格式。

【讨论】:

  • @simon 非常感谢。我根据您的想法发布了完整的解决方案。模板元编程提供了理想的解决方案,这是我怀疑和寻求的解决方案类型。
  • @prestokeys:很高兴它有帮助:-)
【解决方案2】:

感谢 simon 的想法,我现在使用以下特征得到了我想要的理想解决方案:

template <int, int> struct PeriodOrNoPeriod;

template <> struct PeriodOrNoPeriod<0,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<0,1> : std::false_type {};
template <> struct PeriodOrNoPeriod<0,2> : std::true_type {};
template <> struct PeriodOrNoPeriod<0,3> : std::true_type {};

template <> struct PeriodOrNoPeriod<1,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<1,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<1,2> : std::true_type {};
template <> struct PeriodOrNoPeriod<1,3> : std::false_type {};

template <> struct PeriodOrNoPeriod<2,0> : std::true_type {};
template <> struct PeriodOrNoPeriod<2,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<2,2> : std::false_type {};
template <> struct PeriodOrNoPeriod<2,3> : std::true_type {};

template <> struct PeriodOrNoPeriod<3,0> : std::false_type {};
template <> struct PeriodOrNoPeriod<3,1> : std::true_type {};
template <> struct PeriodOrNoPeriod<3,2> : std::false_type {};
template <> struct PeriodOrNoPeriod<3,3> : std::false_type {};

以及一次处理所有情况的功能:

template <System::Action A, int N>
struct System::SetTimeDisplay {
    static void execute (TimeDisplay::Mode mode) {
        constexpr TimeDisplay::Mode M = static_cast<TimeDisplay::Mode>(N);
        if (mode == M)
            timeDisplay = std::make_unique<TimeDisplayClass<M, PeriodOrNoPeriod<A,M>::value>>();
        else
            SetTimeDisplay<A, N+1>::execute(mode);
    }
};

template <System::Action A>
struct System::SetTimeDisplay<A, TimeDisplay::NumTimeDiplayModes> {
    static void execute (TimeDisplay::Mode) {}  // End of recursion
};

template <System::Action A>
inline void System::action (TimeDisplay::Mode mode) {
    SetTimeDisplay<A,0>::execute(mode);
    finalAction<A>();
}

这里有完整的解决方案:

http://ideone.com/6pET9E

【讨论】:

    猜你喜欢
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    • 2014-09-16
    • 1970-01-01
    • 2011-09-07
    • 2021-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多