如何在 C++ 中针对克隆习语创建间谍 class
How to create a spy class against the clone idiom in C++
来自 Java/PHP 世界,我对 C++ 还是个新手。一些用其他语言做的简单事情用 C++ 做起来有点棘手。
我的主要问题如下。现在,我有一个 class(即 "Something"),构造函数为其注入了虚拟 class 依赖项(即 "Base" 的子级)。然后,构造函数将这个注入的实例存储在 unique_ptr<Base>
class 字段中(使用克隆习惯用法)。这在应用程序级别运行良好,一切似乎都按预期运行。这是示例代码:
class Base {
public:
virtual std::unique_ptr<Base> clone() = 0;
virtual void sayHello() const = 0;
};
class Something {
public:
explicit Something(Base &base) { this->base = base.clone(); }
void sayHello() const { base->sayHello(); }
private:
std::unique_ptr<Base> base;
};
但为了确保它确实如此,我编写了单元测试来测试它的行为。在那些测试中,我想断言注入的依赖项方法实际上被调用了。所以从逻辑上讲,注入 "spy" 依赖项应该可以解决问题。
这是我最初做的:
class SpyDerived : public Base {
public:
explicit SpyDerived() = default;
SpyDerived(const SpyDerived &original) { this->someState = original.someState; }
std::unique_ptr<Base> clone() override { return std::make_unique<SpyDerived>(*this); }
void sayHello() const override { std::cout << "My state: " << someState << std::endl; }
void setSomeState(bool value) { this->someState = value; }
private:
bool someState = false;
};
这是我使用的主要功能:
int main() {
SpyDerived derived;
Something something(derived);
derived.setSomeState(true);
something.sayHello();
}
出于显而易见的原因,someState
打印值始终是 false
。我知道 Something
中的 Derived
实例是 Derived
的新副本,不再是在主函数中创建的实例。
所以基本上,我在这里想要实现的是让 Something
class 始终使用在 main 函数中创建的 SpyDerived
实例。有什么办法可以使这项工作。我试图避免仅出于测试目的而更改设计。
我正在使用 MSVC 2015 编译代码。请记住,智能指针、C++ 习语、copy/move 构造函数对我来说都是相当新的概念。
感谢您的帮助。
嗯,你是想克隆你的实例,还是只是引用那个实例?
克隆成语是复制一个class的实例,使新实例独立于旧实例。
根据 PHP:
你基本上是在做这个
<?php
interface Base {
public function sayHello();
}
class SpyDerived implements Base {
private $someState = false;
public function sayHello() {
echo 'My state: ' . $this->someState;
}
}
class Something {
public __construct(Base $base) { $this->base = clone $base; }
public function sayHello() { $this->base->sayHello(); }
private $base = null;
}
$derived = new SpyDerived;
$something = new Something($derived);
$derived->setSomeState(true);
$something->sayHello();
?>
你看到了吗? $base
已克隆。 Something::$base
是一个 copy.
所以在 PHP 中,你会怎样解决这个问题?
简单!删除那个克隆,没有副本!
嗯,在 C++ 中,这是一回事。如果您有一个对象指针并且不想克隆它,请不要实际调用克隆方法。
我们会将您的 class 更改为与 PHP 一样包含对该对象的引用。我们将从让 Something
包含一个非拥有引用开始:
class Something {
public:
explicit Something(Base& b) : base{b} { }
void sayHello() const { base.sayHello(); }
private:
// we simply contain a reference to the base
Base& base;
};
在 C++ 中,引用不拥有对象。如果对象被销毁,所有指向该对象的引用都将指向一个死对象。
如您所见,您的测试保持不变并且有效:
int main() {
SpyDerived derived;
Something something(derived);
derived.setSomeState(true);
something.sayHello();
}
如果您希望 Something
成为 Base
的所有者,请使用 std::unique_ptr<Base>
:
class Something {
public:
explicit Something(std::unique_ptr<Base> b) : base{std::move(b)} { }
void sayHello() const { base->sayHello(); }
private:
std::unique_ptr<Base> base;
};
请注意 base
的 所有权 应该从调用者转移到某物 class。该转移是通过 std::move
事物表达的,因为我们正在转移该资源的 所有权 。
然后在你的测试中:
int main() {
auto derived = std::make_unique<SpyDerived>();
// We want to keep a non-owning reference of derived
// The star (*) operator of std::unique_ptr returns a reference to the pointed object
auto& derived_ref = *derived;
// We transfer the ownership of derived to the `Something`
Something something(std::move(derived));
// Since derived is a reference to the object pointed by our pointer,
// It will affect the value we found in `Something`, because they are
// both pointing to the same instance.
derived.setSomeState(true);
something.sayHello();
}
由于 Something
是 derived
的所有者,如果 something
之前死亡,则非拥有引用 derived_ref
将指向一个死对象。
来自 Java/PHP 世界,我对 C++ 还是个新手。一些用其他语言做的简单事情用 C++ 做起来有点棘手。
我的主要问题如下。现在,我有一个 class(即 "Something"),构造函数为其注入了虚拟 class 依赖项(即 "Base" 的子级)。然后,构造函数将这个注入的实例存储在 unique_ptr<Base>
class 字段中(使用克隆习惯用法)。这在应用程序级别运行良好,一切似乎都按预期运行。这是示例代码:
class Base {
public:
virtual std::unique_ptr<Base> clone() = 0;
virtual void sayHello() const = 0;
};
class Something {
public:
explicit Something(Base &base) { this->base = base.clone(); }
void sayHello() const { base->sayHello(); }
private:
std::unique_ptr<Base> base;
};
但为了确保它确实如此,我编写了单元测试来测试它的行为。在那些测试中,我想断言注入的依赖项方法实际上被调用了。所以从逻辑上讲,注入 "spy" 依赖项应该可以解决问题。
这是我最初做的:
class SpyDerived : public Base {
public:
explicit SpyDerived() = default;
SpyDerived(const SpyDerived &original) { this->someState = original.someState; }
std::unique_ptr<Base> clone() override { return std::make_unique<SpyDerived>(*this); }
void sayHello() const override { std::cout << "My state: " << someState << std::endl; }
void setSomeState(bool value) { this->someState = value; }
private:
bool someState = false;
};
这是我使用的主要功能:
int main() {
SpyDerived derived;
Something something(derived);
derived.setSomeState(true);
something.sayHello();
}
出于显而易见的原因,someState
打印值始终是 false
。我知道 Something
中的 Derived
实例是 Derived
的新副本,不再是在主函数中创建的实例。
所以基本上,我在这里想要实现的是让 Something
class 始终使用在 main 函数中创建的 SpyDerived
实例。有什么办法可以使这项工作。我试图避免仅出于测试目的而更改设计。
我正在使用 MSVC 2015 编译代码。请记住,智能指针、C++ 习语、copy/move 构造函数对我来说都是相当新的概念。
感谢您的帮助。
嗯,你是想克隆你的实例,还是只是引用那个实例?
克隆成语是复制一个class的实例,使新实例独立于旧实例。
根据 PHP:
你基本上是在做这个<?php
interface Base {
public function sayHello();
}
class SpyDerived implements Base {
private $someState = false;
public function sayHello() {
echo 'My state: ' . $this->someState;
}
}
class Something {
public __construct(Base $base) { $this->base = clone $base; }
public function sayHello() { $this->base->sayHello(); }
private $base = null;
}
$derived = new SpyDerived;
$something = new Something($derived);
$derived->setSomeState(true);
$something->sayHello();
?>
你看到了吗? $base
已克隆。 Something::$base
是一个 copy.
所以在 PHP 中,你会怎样解决这个问题?
简单!删除那个克隆,没有副本!
嗯,在 C++ 中,这是一回事。如果您有一个对象指针并且不想克隆它,请不要实际调用克隆方法。
我们会将您的 class 更改为与 PHP 一样包含对该对象的引用。我们将从让 Something
包含一个非拥有引用开始:
class Something {
public:
explicit Something(Base& b) : base{b} { }
void sayHello() const { base.sayHello(); }
private:
// we simply contain a reference to the base
Base& base;
};
在 C++ 中,引用不拥有对象。如果对象被销毁,所有指向该对象的引用都将指向一个死对象。
如您所见,您的测试保持不变并且有效:
int main() {
SpyDerived derived;
Something something(derived);
derived.setSomeState(true);
something.sayHello();
}
如果您希望 Something
成为 Base
的所有者,请使用 std::unique_ptr<Base>
:
class Something {
public:
explicit Something(std::unique_ptr<Base> b) : base{std::move(b)} { }
void sayHello() const { base->sayHello(); }
private:
std::unique_ptr<Base> base;
};
请注意 base
的 所有权 应该从调用者转移到某物 class。该转移是通过 std::move
事物表达的,因为我们正在转移该资源的 所有权 。
然后在你的测试中:
int main() {
auto derived = std::make_unique<SpyDerived>();
// We want to keep a non-owning reference of derived
// The star (*) operator of std::unique_ptr returns a reference to the pointed object
auto& derived_ref = *derived;
// We transfer the ownership of derived to the `Something`
Something something(std::move(derived));
// Since derived is a reference to the object pointed by our pointer,
// It will affect the value we found in `Something`, because they are
// both pointing to the same instance.
derived.setSomeState(true);
something.sayHello();
}
由于 Something
是 derived
的所有者,如果 something
之前死亡,则非拥有引用 derived_ref
将指向一个死对象。