C++ 抽象基类构造函数/析构函数 - 一般正确性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8513408/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ abstract base class constructors/destructors - general correctness
提问by Stuart Blackler
Recently I have dumb as a developer, so I took the plunge, got a C++ book and learning how to do things properly. In my head, I know what I would like to do. I effectively want an Interface
that when inherited, must be overridden (if this is possible?). So far, I have the following:
最近我作为一名开发人员很笨,所以我冒险尝试,买了一本 C++ 书并学习如何正确地做事。在我的脑海中,我知道我想做什么。我实际上想要一个Interface
继承时,必须被覆盖(如果这可能?)。到目前为止,我有以下几点:
class ICommand{
public:
// Virtual constructor. Needs to take a name as parameter
//virtual ICommand(char*) =0;
// Virtual destructor, prevents memory leaks by forcing clean up on derived classes?
//virtual ~ICommand() =0;
virtual void CallMe() =0;
virtual void CallMe2() =0;
};
class MyCommand : public ICommand
{
public:
// Is this correct?
MyCommand(char* Name) { /* do stuff */ }
virtual void CallMe() {}
virtual void CallMe2() {}
};
I have purposely left how I think the constructor/destructor's should be implemented in ICommand
. I know if I remove the comments, it will not compile. Please could someone:
我故意离开了我认为构造函数/析构函数应该在ICommand
. 我知道如果我删除评论,它将无法编译。请有人:
- Show me how to declare the constructor/destructor's in
ICommand
and how they are meant to be used inMyCommand
- Have I set things up correctly in
ICommand
so thatMyCommand
must overrideCallMe
andCallMe2
.
- 告诉我如何声明构造函数/析构函数的 in
ICommand
以及它们的用途MyCommand
- 我是否正确设置了内容,
ICommand
以便MyCommand
必须覆盖CallMe
和CallMe2
.
I hope I haven't missed something really simple...
我希望我没有错过一些非常简单的东西......
回答by e.James
C++ does not allow for virtual constructors. A simple implementation (without the virtual constructor) would look something like this:
C++ 不允许使用虚拟构造函数。一个简单的实现(没有虚拟构造函数)看起来像这样:
class ICommand {
public:
virtual ~ICommand() = 0;
virtual void callMe() = 0;
virtual void callMe2() = 0;
};
ICommand::~ICommand() { } // all destructors must exist
Note that even a pure virtual destructor mustbe defined.
请注意,即使是纯虚拟析构函数也必须定义。
A concrete implementation would look exactly like your example:
一个具体的实现看起来和你的例子完全一样:
class MyCommand : public ICommand {
public:
virtual void callMe() { }
virtual void callMe2() { }
};
You have a couple of optionsfor the constructor. One option is to disable the default constructor for ICommand
, so that subclasses will haveto implement a constructor that calls your ICommand constructor:
构造函数有几个选项。一种选择是禁用 的默认构造函数ICommand
,以便子类必须实现一个调用 ICommand 构造函数的构造函数:
#include <string>
class ICommand {
private:
const std::string name;
ICommand();
public:
ICommand(const std::string& name) : name(name) { }
virtual ~ICommand() = 0;
virtual void callMe() = 0;
virtual void callMe2() = 0;
};
ICommand::~ICommand() { } // all destructors must exist
A concrete implementation would now look something like this:
一个具体的实现现在看起来像这样:
class MyCommand : public ICommand {
public:
MyCommand(const std::string& name) : ICommand(name) { }
virtual void callMe() { }
virtual void callMe2() { }
};
回答by Theo
I know this one is old, but it is still my first hit on this issue. This is how I would do it.
我知道这是旧的,但它仍然是我在这个问题上的第一次打击。这就是我要做的。
Interface header foo.h:
接口头文件 foo.h:
#pragma once
#include <memory>
enum class Implementations {Simple, Fancy};
class Foo
{
public:
using Ptr = std::unique_ptr<Foo>;
virtual ~Foo() = default;
virtual void do_it() = 0;
};
Foo::Ptr create_foo(Implementations impl); // factory
Yes I know that "pragma once" is strictly speaking not standard, but it works for me.
是的,我知道“pragma once”严格来说不是标准的,但它对我有用。
Note that nothing is implemented here. There is no constructor: an abstract class can not be instantiated. You get a pointer to the interface through the factory. For the virtual function calls to work, they must be called through a pointer. The virtual destructor is defaulted because it doesn't have to do anything special except polymorphing to the implementation. The factory is a free function. No need to try to make it a static member or something like that. This is not java.
请注意,这里没有实现任何内容。没有构造函数:抽象类不能被实例化。您可以通过工厂获得一个指向接口的指针。要使虚函数调用起作用,必须通过指针调用它们。虚拟析构函数是默认的,因为它不需要做任何特殊的事情,除了对实现进行多态。工厂是一个免费的功能。无需尝试使其成为静态成员或类似的东西。这不是java。
Interface foo.cpp:
接口 foo.cpp:
#include "foo.h"
#include "foo_impl.h"
Foo::Ptr create_foo(Implementations impl)
{
switch (impl)
{
case Implementations::Simple:
return std::make_unique<Simple_foo>();
case Implementations::Fancy:
return std::make_unique<Fancy_foo>();
default:
return nullptr;
}
}
Here the factory is implemented. Notice that the factory has to know the implementation(s). That is why we don't implement it inline: if it was inline, the interface header would have to include the implementation header, and through it, knowledge of the implementation would "leak out" to the callsite.
这里实现了工厂。请注意,工厂必须知道实现。这就是我们不内联实现它的原因:如果它是内联的,则接口标头必须包含实现标头,并且通过它,实现的知识将“泄漏”到调用站点。
The implementation header foo_impl.h:
实现头文件 foo_impl.h:
#pragma once
#include "foo.h"
class Simple_foo : public Foo
{
void do_it() override;
};
class Fancy_foo : public Foo
{
void do_it() override;
};
Nothing special, just override the virtual functions of the interface. Because this exaple is simple, I put both implementations in the same files. In real applications that will be different.
没什么特别的,只是覆盖了接口的虚函数。因为这个例子很简单,我把两个实现放在同一个文件中。在实际应用中,情况会有所不同。
The implementation foo_impl.cpp:
foo_impl.cpp 的实现:
#include "foo_impl.h"
#include <iostream>
void Simple_foo::do_it()
{
std::cout << "simple foo\n";
}
void Fancy_foo::do_it()
{
std::cout << "fancy foo\n";
}
Just implement the functions.
只需实现功能。
The main.cpp:
main.cpp:
#include "foo.h"
int main()
{
auto sf = create_foo(Implementations::Simple);
sf->do_it();
auto ff = create_foo(Implementations::Fancy);
ff->do_it();
return 0;
}
Through the enum we can select the implementation we want. The pointers are of type Foo::Ptr
, an alias for std::unique_ptr<Foo>
. The callsite has no knowledge of the implementation at all, only the interface.
通过枚举我们可以选择我们想要的实现。指针的类型是 Foo::Ptr
,是 的别名std::unique_ptr<Foo>
。调用站点根本不知道实现,只知道接口。
The output will be as expected:
输出将如预期的那样:
simple foo
fancy foo