使用头/源文件来分离接口和实现
Use of header/source files to separate interface and implementation
在c++中,类通常这样声明:
// Object.h
class Object
{
void doSomething();
}
// Object.cpp
#include "Object.h"
void Object::doSomething()
{
// do something
}
我理解这改善了编译时间,因为将类放在一个文件中使您无论何时更改实现或接口(参见此)都可以重新编译它。
然而,从面向对象的角度来看,我看不出将接口与实现分离有什么帮助。我读过很多其他的问题和答案,但我的问题是,如果你定义一个类的方法正确(在单独的头/源文件),那么你怎么能做一个不同的实现?如果在两个不同的地方定义Object::method,那么编译器如何知道调用哪一个?在不同的命名空间中声明Object::方法定义吗?
如果你想在同一个程序中使用一个接口和多个实现,那么你可以使用抽象虚基。
一样:
class Printer {
public:
virtual void print_string(const char *s) = 0;
virtual ~Printer();
};
然后你可以有实现:
class EpsonPrinter : public Printer {
public:
void print_string(const char *s) override;
};
class LexmarkPrinter : public Printer {
public:
void print_string(const char *s) override;
};
另一方面,如果您正在查看实现操作系统独立性的代码,则可能有几个子目录,每个子目录对应一个操作系统。头文件是相同的,但Windows的源文件仅为Windows构建,Linux/POSIX的源文件仅为Linux构建。
然而,从[一个]面向对象的角度来看,我看不出将接口与实现分离有什么帮助。
从面向对象的角度来看,它不能帮助,也不打算这样做。这是c++的一个文本包含特性,它继承自C语言,而C语言并不直接支持面向对象编程。
模块化的文本包含是一个从汇编语言借来的特性。它几乎是面向对象编程的对立面,或者基本上是计算机程序组织领域中任何好的东西的对立面。文本包含允许c++编译器与不存储任何符号类型信息的古老对象文件格式进行互操作。Object.cpp
文件被编译为这种对象格式,从而产生Object.o
文件或Object.obj
文件或您的平台上的其他文件。当程序的其他部分使用这个模块时,它们几乎完全信任在Object.h
中写的关于它的信息。除了带有偏移量和大小等数字信息的符号外,Object.o
文件中没有任何有用的内容。如果头文件中的信息不能正确地反映Object.obj
,你就会有未定义的行为(在某些情况下,通过c++对函数重载的支持来缓解,这将使不匹配的函数调用变成不可解析的符号,这要归功于名称混淆)。
例如,如果头文件声明了一个变量extern int foo;
,但目标文件是编译double foo = 0.0;
的结果,这意味着程序的其余部分正在访问double
对象作为int
。防止这种情况发生的原因是Object.cpp
包含自己的头文件(从而迫使编译器捕获声明和定义之间的不匹配),并且您有一个合理的构建系统,确保如果有任何内容涉及Object.h
,则重建Object.cpp
。如果该检查是基于时间戳的,那么您还必须有一个正常的文件系统和版本控制系统,它们不会对时间戳做奇怪的事情。
如果你在两个不同的地方定义Object::method,那么编译器如何知道调用哪一个?
它不会,事实上,如果你这样做,你将违反"一个定义规则",根据标准,这将导致未定义的行为,不需要诊断。
如果你想为一个类接口定义多个实现,你应该以某种方式使用继承。
一种可能的方法是,使用虚基类并覆盖不同子类中的一些方法。
如果希望将类的实例作为值类型操作,那么可以使用pImpl习惯用法,并结合虚拟继承。因此,您将有一个类,即"指针"类,它公开接口,并持有指向抽象虚拟基类类型的指针。然后,在.cpp文件中,您将定义虚拟基类,并定义它的多个子类,pImpl类的不同构造函数将实例化不同的子类作为实现。
如果您想使用静态多态性,而不是运行时多态性,您可以使用CRTP习惯用法(它最终仍然基于继承,而不是虚拟继承)。
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- Visual C++GC接口如何启用它以及要包含哪个库
- Windows.h与GLFW.h的接口
- 当字段可以为null时,如何使用C++接口在Avro中写入数据
- 提供与TMP和SFINAE的通用接口
- 为重写std::exception的库生成swig接口时出错
- 内联如何影响模块接口中的成员函数
- C++:如何读取分离变量,然后读取向量
- 分离一个静态常量 std::thread?
- .h 和.cpp文件分离时出错,但仅使用 .h 文件时没有错误.我做错了什么?
- COM 接口 c# 封送数组数组
- 如何在 SCIP C++ 接口中获取 MILP 约束矩阵中的系数值
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 如何在 c++ 中将数据与文件流分离
- 如何绑定 C++ gRPC 客户端的网络接口
- 将接口与实现分离不起作用 c++
- 无法分离实现和接口
- 继承层次结构中接口与实现的分离(C++新手)
- C++在模板类中的友好关系,接口与实现的分离
- 使用头/源文件来分离接口和实现