【问题标题】:Allow a mock class to inherit from a final class允许模拟类从最终类继承
【发布时间】:2014-10-20 12:02:50
【问题描述】:

我们可以使用新的 C++ 关键字 final 声明一个最终/密封的不可继承类。

class Generator final
{

};

这个类可能继承自其他类,可能有也可能没有 virtual(继承与否)。但是,如何使它成为final,却又允许一个类继承它呢?

我们主要需要从真实类派生一个模拟类(有或没有后期绑定,因此virtual 并不重要)。如何使其工作:

class MockGenerator : Generator{};

但不允许任何其他继承?

【问题讨论】:

  • 我不认为你可以。一旦类/结构是最终的,就是这样,游戏继承方式
  • 我同意 - 我不认为在 C++11 中你可以覆盖 'final' 关键字。如果它是最终的 - 它是最终的。但是,也许您可​​以考虑使类不可继承的其他方法(取决于您要如何使用它):parashift.com/c++-faq/final-classes.html
  • 另一个问题:为什么要模拟最后一堂课?你不应该模拟一个界面,以防你的architecture is ok

标签: c++ unit-testing c++11 c++14


【解决方案1】:

但是,如何使它成为最终的,但允许一个类继承它?

这是不可能的。

我们主要需要从真实类派生一个模拟类(有或没有后期绑定,因此虚拟并不重要)。

如果类是最终类,您不需要从它派生。如果你确实需要从它派生,它不是最终的。选择一个。

编辑:您可以为您的类添加限制,但这些限制会为界面带来成本:

class Generator // not final
{
    Generator(); // the only accessible constructor is private

    // whitelist who has access to this constructor
    friend class MockGenerator;
public:
    // no public constructors here except for copy & move

    Generator(Generator&);
    Generator(Generator&&);
    ...

    // provide controlled access to the private constructor
    static Generator make_generator() { return Generator(); }

    // rest of API here
};

这是一个允许它的工厂和 MockGenerator 特化调用它的构造函数的类。不过,这是以阻止琐碎的构造为代价的。

旧代码(不再可编译):

Generator instance;

新代码(由私有构造函数强制执行):

auto instance = Generator::make_generator();

【讨论】:

    【解决方案2】:

    一种可能:对final使用define,在生成测试环境时定义为空。

    #ifdef MOCK
    #define CLASS_FINAL
    #else
    #define CLASS_FINAL final
    #endif
    

    编辑:我同意 utnapistim 的评论:这不是建议,只是技术上的可能性(但至少比 #define final 更好)。

    【讨论】:

    • 请不要推荐这个。这是一个测试与生产代码不同的代码的解决方案(并且 - 根据定义 - 不是您最终交付的代码)。如果编写的代码不可测试,那是一个设计问题,应该在设计级别解决,而不是在“破解关键字的含义”级别(我在 SO 上看到了类似的解决方案,提出 #define private public 作为不可测试代码的解决方法) .
    • 请注意,编译器根据final 生成的代码可能有所不同——指向final 方法或类的指针可以通过它去虚拟化所有调用。例如,这可能会导致以前没有发生的内联。内联函数在不同 TU 中看起来不同的地方可以暴露 UB。在类的主体中定义的方法是隐式内联的。加上性能变化。
    【解决方案3】:

    如果你需要创建模拟类来进行单元测试,那么你可以试试Typemock Isolator++。因为它可以轻松处理final 类。您甚至不需要更改生产代码中的某些内容(例如创建单独的模拟类)。我创建了简单的测试来演示它:

    class Generator final
        {
            public:
                int foo()
                {
                    return 0;
                }
        };
    
        TEST_METHOD(TestFinal)
        {
            Generator* generator = FAKE<Generator>();
            WHEN_CALLED(generator->foo()).Return(1);
    
            int result = generator->foo();
    
            Assert::AreEqual(1, result);
        }
    

    希望对你有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多