避免在C++中重复子类定义
Avoiding repetitive sub-class definitions in C++
我是C++类的新手,我有一个关于定义抽象类型/接口的多个子类的问题,这些子类将具有相同的定义。
以以下示例为例,该示例可能出现在具有 3 个子类的头文件中:
class Animal {
private:
int a;
int b;
public:
explicit Animal(int a) {}
virtual Animal* getFriend() = 0;
virtual bool walk() = 0;
virtual bool talk() = 0;
virtual bool someFunction() = 0;
virtual bool someOtherFunction() = 0;
// ... many more functions
}
class Zebra: public Animal {
Animal* getFriend();
bool walk();
bool someFunction();
bool someOtherFunction();
// ... continues for many more functions
}
class Cow: public Animal {
Animal* getFriend();
bool walk();
bool someFunction();
bool someOtherFunction();
// ... continues for many more functions
}
class Salmon: public Animal {
Animal* getFriend();
bool walk();
bool someFunction();
bool someOtherFunction();
// ... continues for many more functions
}
// ... many more animals
像这样声明子类似乎是重复的,并且可能容易出错。由于除了类的名称外,类定义是相同的,因此是否有更有效的方法来批量声明动物子类?
在我工作的上下文中,每个动物都会在单独的.cpp
文件中有一个完全独立的实现。
如果我完全错误,请告诉我。任何帮助将不胜感激。
当您不为每个被覆盖的虚拟类成员函数使用override
关键字时,它确实容易出错。
而不是像这样声明派生类函数
bool someFunction();
你可以/应该这样声明它
bool someFunction() override;
这样,如果声明与基类签名不匹配,则会收到编译错误。没有它,你将拥有一个非常好的可编译程序,但有一个行为错误。
除此之外,你的策略很好,是处理抽象函数的方法。
我正在写另一个答案作为替代解决方案。实际上,如果我遇到相同的"问题"或"问题",我不会声明为批量,我只会创建zebra.h
,zebra.cpp
,从Animal
继承并单独声明/定义所有成员。换句话说,我宁愿不聪明,但如果你想成为下面的代码,可以是一个替代方案。
实际上,您只想从模板创建类声明。这就是template
正在做的事情。可以用MACRO
模仿相同的行为,但我宁愿template
而不是MACRO
,因为这是Bjarne所做的。
所以这是代码
动物.H
#ifndef ANIMAL_H
#define ANIMAL_H
class Animal {
private:
int a;
int b;
public:
explicit Animal(int a) {}
virtual ~Animal() = default; // You should this virtual destructor
// for polymorphic types.
virtual Animal* getFriend() = 0;
virtual bool walk() = 0;
virtual bool talk() = 0;
virtual bool someFunction() = 0;
virtual bool someOtherFunction() = 0;
};
enum class animal_types
{
zebra ,
cow ,
salmon ,
special_animal
};
template< animal_types >
struct ugly_bulk_animal_inheritor : Animal
{
using Animal::Animal; // Use parent constructor as is
Animal* getFriend() override;
bool walk() override;
bool talk() override;
bool someFunction() override;
bool someOtherFunction() override;
};
using Zebra = ugly_bulk_animal_inheritor< animal_types::zebra >;
using Cow = ugly_bulk_animal_inheritor< animal_types::cow >;
using Salmon = ugly_bulk_animal_inheritor< animal_types::salmon >;
// So on..
#include "zebra.h"
#include "salmon.h"
#include "cow.h"
#include "special_animal.h"
#endif // ANIMAL_H
牛·
#ifndef COW_H
#define COW_H
#include "animal.h"
template<>
Animal* Cow::getFriend() {
return nullptr;
}
template<>
bool Cow::walk() {
return true;
}
template<>
bool Cow::talk() {
return false;
}
template<>
bool Cow::someFunction() {
return true;
}
template<>
bool Cow::someOtherFunction() {
return true;
}
#endif // COW_H
鲑鱼
#ifndef SALMON_H
#define SALMON_H
#include "animal.h"
template<>
Animal* Salmon::getFriend() {
return nullptr;
}
template<>
bool Salmon::walk() {
return true;
}
template<>
bool Salmon::talk() {
return true;
}
template<>
bool Salmon::someFunction() {
return true;
}
template<>
bool Salmon::someOtherFunction() {
return true;
}
#endif // SALMON_H
斑马
#ifndef ZEBRA_H
#define ZEBRA_H
#include "animal.h"
template<>
Animal* Zebra::getFriend() {
return nullptr;
}
template<>
bool Zebra::walk() {
return true;
}
template<>
bool Zebra::talk() {
return false;
}
template<>
bool Zebra::someFunction() {
return true;
}
template<>
bool Zebra::someOtherFunction() {
return true;
}
#endif // ZEBRA_H
special_animal.h
#ifndef SPECIAL_ANIMAL_H
#define SPECIAL_ANIMAL_H
#include "animal.h"
#include <iostream>
template<>
struct ugly_bulk_animal_inheritor<animal_types::special_animal> : Animal
{
using Animal::Animal; // Use parent constructor as is
Animal* getFriend() override { return nullptr; }
bool walk() override { return true; }
bool talk() override { return true; }
bool someFunction() override { return true; }
bool someOtherFunction() override { return true; }
void specility_fn() {
std::cout << "A speciality" << std::endl;
}
private:
int some_extra_member;
// etc..
};
using special_animal = ugly_bulk_animal_inheritor<animal_types::special_animal>;
#endif // SPECIAL_ANIMAL_H
主.cpp
#include <iostream>
#include "animal.h"
int main(int argc, char *argv[])
{
Animal* instance;
Zebra z { 5 };
Cow c { 6 };
Salmon t { 7 };
instance = &z;
std::cout << "Zebra can talk ? " << instance->talk() << std::endl;
instance = &t;
std::cout << "Salmon can talk ? " << instance->talk() << std::endl;
special_animal s { 5 };
s.specility_fn();
return 0;
}
除了使用宏来定义类(这更糟!),你可能没有很多事情可以做。 偶尔这样的东西可能会起作用,但我敢打赌,在某个时候,你会想要专门研究其中一种动物,导致你再次放弃宏。正是出于这个原因,我会避免使用这种特定的技术。
#define DECLARE_ANIMAL(ANIMAL_TYPE)
class ANIMAL_TYPE: public Animal {
Animal* getFriend() override;
bool walk() override;
bool someFunction() override;
bool someOtherFunction() override;
};
DECLARE_ANIMAL(Zebra);
DECLARE_ANIMAL(Cow);
DECLARE_ANIMAL(Salmon);
一般来说,尝试将尽可能多的重复类方法和数据移动到基类中,以尽量减少代码重复的数量。不过,这可能需要您思考问题的方式略有改变......
例如,walk()。在牛/斑马/马/猫/狗的情况下,行走的行为几乎相同。唯一真正的差异可以用数据来衡量(例如步行速度,使用多少条腿,步行的步态是多少,每步有多大?如果能够以数据驱动的方式定义行为,则只需在 Derived 类构造函数中设置这些参数,并避免需要 定制方法。以这种方式进行类设计还有其他一些好处,例如,您将有一个"Dog"类,但它能够表示 4 条腿的狗和一条 3 条腿的狗,而无需创建新类。
无论如何,这通常是我推荐的方法...
- 避免在C++中重复子类定义
- 使用子类覆盖基类中定义的函数
- C++ 确保子类为常量提供自定义值
- 如何为定义的情况创建子类?
- 如何在子类中重新定义数组大小?
- 无法使用在子类中定义的虚拟getter实现基类
- 运算符的歧义错误<<自定义 std::ostream 子类中的重载
- 是否有 lint 工具可以检查子类虚拟函数是否与父类定义匹配?
- 使用子类中的类型定义扩展模板类
- 模板化类继承的子类定义中的错误
- 从模板化类中的子类访问定义
- 使用基类构造函数的子类构造函数的大纲定义
- 共享库中非模板基的模板子类导致未定义的符号类型信息'class'链接错误
- 在子类中定义可变参数函数专用化
- 如何使函数返回由子类定义的值?C++
- 子类ConstexPR类以获得不同的用户定义的转换行为
- 为什么要将子类定义为其自己的父类的好友
- 从超类函数返回子类定义
- 如何为子类定义泛型模板化创建函数
- c++超类为子类定义静态成员变量