在C++中,如果我们在某个基类中有一些虚函数(比如Base),我们想要覆盖这个虚函数,我们将不得不再次声明这个虚函数,使其在我们的派生类中编译.
class Base {
public:
virtual void virtualFunction();
static int s_whatSoEver[];
private:
void _privateFunction();
}
class Derived {
public:
virtual void virtualFunction();
}
Run Code Online (Sandbox Code Playgroud)
这不是愚蠢的,因为如果我们想改变虚函数原型,我们必须改变derived-s的每个声明吗?
此外,为什么有必要在头文件中声明一些受保护或私有函数,因为头文件用于公共接口定义,而使用此接口的用户根本不需要关心它们?也许我们可以像Objective-C一样直接在.cpp文件中实现和声明私有或受保护的函数.
C++也没有静态初始化器,如果我们想要初始化一些静态类变量,我们必须为此创建一个类:
class BaseStaticVariableInitializer {
public:
BaseStaticVariableInitializer() {
Base::s_whatSoEver = new int[20];
for (int i = 0; i < 20; i++) {
s_whatSoEver[i] = xxx;
}
}
~BaseStaticVariableInitializer() {
delete [] Base::s_whatSoEver;
}
}
Run Code Online (Sandbox Code Playgroud)
并特别为它初始化一个静态类常量:
static BaseStaticVariableInitializer s_baseStaticVariableInitializer;
Run Code Online (Sandbox Code Playgroud)
对不起我的无知,但是你正确的方法来编写你的c ++代码以适应DRY?
这不是愚蠢的,因为如果我们想改变虚函数原型,我们必须改变derived-s的每个声明吗?
否.如果要更改基类中的虚函数原型,则需要更改公共接口.你不应该这样做.
此外,为什么有必要在头文件中声明一些受保护或私有函数,因为头文件用于公共接口定义,而使用此接口的用户根本不需要关心它们?
protected成员应被视为类的公共接口的一部分.它protected可以帮助您避免因使用它们而导致的风险public.但不要搞错:protected成员是类接口的一部分,应该这样对待.
关于private头文件中的s:是的,我同意,在许多方面,将它们仅保留在实现文件中更合乎逻辑.但是,请考虑何时按值传递类:
foo(Bacon b)
{
b.cook();
}
Run Code Online (Sandbox Code Playgroud)
要调用foo(),您需要为Bacon编译器提供完整的类定义.(注意:只有类定义,而不是其成员函数的定义.)这样编译器可以知道在调用时为类分配多少堆栈空间foo().如果编译器也必须搜索实现文件以查找private变量,那么解析会更复杂(并且编译可能会更慢).
既然你提到了DRY,我必须指出这一点.DRY原则规定:
每一段知识都必须在系统中具有单一,明确,权威的表示.
在相关类中声明虚函数并不违反此原则.它们的功能不同.在Base,你说的Base::foo是virtual.在Derived,你说的Derived::foo也是virtual.Base::foo并且Derived::foo是两个独立的函数,但恰好可以通过指针或对a的引用来调用它们Base.
这不是愚蠢的,因为如果我们想改变虚函数原型,我们必须改变derived-s的每个声明吗?
这正是重点.如果签名在基础中发生更改,您希望编译器告诉您,而不是可能尝试使用错误的类型调用该函数.C++是一种静态类型的编译语言.类型在编译时定义,如果虚函数的类型发生更改,则需要重新编译以适应更改.
为什么有必要在头文件中声明一些protected或private函数,因为头文件用于公共接口定义,而使用这个接口的用户根本不需要关心它们?
这也是完全相同的设计选择.在C++中,单定义规则要求在所有翻译单元(不同的编译文件)中定义每种类型完全相同.如前所述,C++是一种编译语言,通常成员会影响类,而不管访问说明符(在编译过程中被删除).当编译器创建您的类型的对象时,它必须为每个和所有数据成员分配足够的空间,无论是公共,私有还是受保护.在构建虚拟表时,需要知道需要为所有功能分配多少个插槽.可以说,非虚函数不会影响生成的对象/ RTTI,但它们可能会影响生成的对象/ RTTI.
如果在基类中添加了一个新的虚函数,其具有与派生类中的protected/private成员函数完全相同的签名,则后者将成为前者的覆盖,并且需要在虚拟表中创建新的插槽.虽然这可能不太可能,但如果功能隐藏在单个翻译单元(您可能或无法访问)中,您可能会遇到这些问题.
C++也没有静态初始化器,如果我们想要初始化一些静态类变量,我们必须为此创建一个类
C++没有静态初始化程序,但我肯定不会为它创建一个类.静态成员变量需要在单个翻译单元中定义,并且在该翻译单元中可以初始化它们.在简单的情况下,您可以直接进行常规初始化,对于更复杂的情况,您可以创建一个提供初始化值的函数.
int *Base::member = new int[10](); // value initialized (set to 0)
// abusing lambdas not to write a function:
int *Base::member2 = []()->int* {
int *p = new int[10];
for (int i = 0; i < 10; ++i) p[i] = xxx;
return p; }();
Run Code Online (Sandbox Code Playgroud)
请注意,这不会控制资源的释放(您在代码中执行此操作),但可以使用语言结构轻松处理:
std::unique_ptr<int[]> Base::member(new int[10]());
Run Code Online (Sandbox Code Playgroud)