在 C++ 中避免重复的 sub-class 定义
Avoiding repetitive sub-class definitions in C++
我是 C++ classes 的新手,我有一个关于定义抽象 type/interface 的多个子classes 的问题,它们将具有相同的定义。
以下面的例子为例,它可能出现在一个包含 3 个子 classes 的头文件中:
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
像这样声明子classes 似乎是重复的并且可能容易出错。因为 class 定义除了 class 的名称外都是相同的,是否有更有效的方法来批量声明动物子 classes?
在我工作的上下文中,每只动物都会在单独的 .cpp
文件中有一个完全独立的实现。
如果我的处理方式完全错误,请告诉我。任何帮助将不胜感激。
它 是 当您不为每个重写的虚拟 class 成员函数使用 override
关键字时确实容易出错。
而不是像这样声明派生的 class 函数
bool someFunction();
你can/should这样声明
bool someFunction() override;
这样,如果声明与基础 class 签名不匹配,就会出现编译错误。没有它,您将拥有一个非常好的可编译程序,但有一个行为错误。
除此之外,你的策略很好,是处理抽象函数的方法。
如果不使用宏来定义 classes(这更糟!),您可能无能为力。偶尔这样的东西可能会起作用,但我敢打赌,在 某些 点,你会想要专门化其中一种动物,导致你再次放弃宏。出于这个原因,我会避免使用该特定技术。
#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);
一般来说,尝试将尽可能多的重复 class 方法和数据移动到基础 class 中,以尽量减少代码重复量。不过,这可能需要您对问题的思考方式稍作改变....
例如walk()。在 Cow/Zebra/Horse/Cat/Dog 的情况下,行走的动作几乎相同。唯一真正的差异可以通过数据(例如步行速度,使用多少条腿,步行的步态是多少,每步幅有多大?)来衡量。如果您可以以数据驱动的方式定义行为,则只需在 Derived class 构造函数中设置这些参数,并避免需要
定制的方法。以这种方式处理 class 设计还有一些其他好处,例如,您将有一个 'Dog' class,但它可以代表一条 4 条腿的狗,以及一条 3有腿狗,无需创建新的 class。
无论如何,这通常是我推荐的方法...
我正在写另一个答案作为替代解决方案。实际上,如果我面对相同的 'issue' 或 'problem',我不会声明为批量,我只会创建 zebra.h
、zebra.cpp
,继承自 Animal
和declare/define 所有成员单独。换句话说,我宁愿不聪明,但如果你想成为下面的代码片段可能是另一种选择。
实际上,您只想从模板创建一个 class 声明。这就是 template
正在做的事情。可以用 MACRO
s 模仿相同的行为,但我更喜欢 template
而不是 MACRO
因为这是 Bjarne 所做的。
所以这是代码
animal.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
cow.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
salmon.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
zebra.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
main.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;
}
我是 C++ classes 的新手,我有一个关于定义抽象 type/interface 的多个子classes 的问题,它们将具有相同的定义。
以下面的例子为例,它可能出现在一个包含 3 个子 classes 的头文件中:
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
像这样声明子classes 似乎是重复的并且可能容易出错。因为 class 定义除了 class 的名称外都是相同的,是否有更有效的方法来批量声明动物子 classes?
在我工作的上下文中,每只动物都会在单独的 .cpp
文件中有一个完全独立的实现。
如果我的处理方式完全错误,请告诉我。任何帮助将不胜感激。
它 是 当您不为每个重写的虚拟 class 成员函数使用 override
关键字时确实容易出错。
而不是像这样声明派生的 class 函数
bool someFunction();
你can/should这样声明
bool someFunction() override;
这样,如果声明与基础 class 签名不匹配,就会出现编译错误。没有它,您将拥有一个非常好的可编译程序,但有一个行为错误。
除此之外,你的策略很好,是处理抽象函数的方法。
如果不使用宏来定义 classes(这更糟!),您可能无能为力。偶尔这样的东西可能会起作用,但我敢打赌,在 某些 点,你会想要专门化其中一种动物,导致你再次放弃宏。出于这个原因,我会避免使用该特定技术。
#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);
一般来说,尝试将尽可能多的重复 class 方法和数据移动到基础 class 中,以尽量减少代码重复量。不过,这可能需要您对问题的思考方式稍作改变....
例如walk()。在 Cow/Zebra/Horse/Cat/Dog 的情况下,行走的动作几乎相同。唯一真正的差异可以通过数据(例如步行速度,使用多少条腿,步行的步态是多少,每步幅有多大?)来衡量。如果您可以以数据驱动的方式定义行为,则只需在 Derived class 构造函数中设置这些参数,并避免需要 定制的方法。以这种方式处理 class 设计还有一些其他好处,例如,您将有一个 'Dog' class,但它可以代表一条 4 条腿的狗,以及一条 3有腿狗,无需创建新的 class。
无论如何,这通常是我推荐的方法...
我正在写另一个答案作为替代解决方案。实际上,如果我面对相同的 'issue' 或 'problem',我不会声明为批量,我只会创建 zebra.h
、zebra.cpp
,继承自 Animal
和declare/define 所有成员单独。换句话说,我宁愿不聪明,但如果你想成为下面的代码片段可能是另一种选择。
实际上,您只想从模板创建一个 class 声明。这就是 template
正在做的事情。可以用 MACRO
s 模仿相同的行为,但我更喜欢 template
而不是 MACRO
因为这是 Bjarne 所做的。
所以这是代码
animal.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
cow.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
salmon.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
zebra.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
main.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;
}