比方说我要开发一个日志功能。在 logging.h
我把这个函数声明为以后的应用所使用。
// logging.h
#include <string>
namespace logging {
void LogThis(const std::string& text);
}; // namespace logging
它的定义显然是在 logging.cpp
:
// logging.cpp
void logging::LogThis(const std::string& text) {
std::cout << "Log: " << text << '\n';
}
现在让我们假装我的 LogThis
函数的工作被分割成一些较小的辅助函数。它们不是日志接口的一部分。让我们来看看 Prettify
函数为例。
// logging.cpp
void logging::LogThis(const std::string& text) {
Prettify(text);
std::cout << "Log: " << text << '\n';
}
我的问题是:我应该把函数的声明放在哪里?Prettify
? 我不应该把它包括在 logging.h
头文件,因为这样它就可以被其他编译单元调用,而且它不是接口的一部分。所以只要把它放在 logging.cpp
而不是像这样?
// logging.cpp
namespace logging {
void Prettify(std::string& ugly_text);
void LogThis(const std::string& text) {
Prettify(text);
std::cout << "Log: " << text << '\n';
}
void Prettify(std::string& ugly_text) {
// making it pretty...
}
}
我正在寻找一些最佳实践经验规则的意见:) 先谢谢你
对于那些 只是 文件中需要,我只需要把它放在C++文件本身的匿名命名空间中,有点像现代的传统C++文件中的 static
功能关键字(a):
namespace {
void WeaveMagic(std::string& ugly_text) {
WeaveMoreMagic(ugly_text);
}
void Prettify(std::string& ugly_text) {
WeaveMagic(ugly_text);
}
}
如果你把它放在任何函数的使用之前,并确保调用的严格层次,你可以跳过声明,因为定义提供了所需的信息,如上图所示。
当然,如果有任何 圆形 多个匿名函数之间的依赖关系(即循环递归),你仍然需要提供声明。
#include <iostream>
namespace {
int DivThree(int val); // needed to implement AddOne()
int AddOne(int val) {
std::cout << "AddOne " << val << " -> " << (val + 1) << '\n';
if (val > 0) return DivThree(val + 1);
return val;
}
int DivThree(int val) {
std::cout << "DivThree " << val << " -> " << (val / 3) << '\n';
return AddOne(val / 3);
}
}
int main(){
int final = AddOne(18);
std::cout << "Final " << final << '\n';
return 0;
}
而且,是的,这就是 很 人为的,但循环递归的好例子却少之又少:-) 输出的是
AddOne 18 -> 19
DivThree 19 -> 6
AddOne 6 -> 7
DivThree 7 -> 2
AddOne 2 -> 3
DivThree 3 -> 1
AddOne 1 -> 2
DivThree 2 -> 0
AddOne 0 -> 1
Final 0
(a) CPP核心指导线SF.22实际上涵盖了这一点。
对所有内部不导出的实体使用未命名(匿名)的命名空间。
理由是 任何外部实体都不能依赖于嵌套的未命名命名空间中的实体。考虑将实现源文件中的每个定义都放在未命名的命名空间中,除非那是在定义一个 "externalexported "实体。
一个API类和它的成员不能住在一个未命名的命名空间中;但任何在实现源文件中定义的 "帮助者 "类或函数都应该在一个未命名的命名空间范围内。
如果你只对函数进行操作,就像@paxdiablo写的那样,你可以使用匿名命名空间(看他的回答)。
我有一些基于C语言的习惯,所以个人也会认为是 static
函数。但我不知道C++狂热者会怎么看:)。static
(在这种情况下)使编译单元(logging.cpp)的函数成为本地的,所以它不能从外部链接。
//logging.cpp
static void Prettify(std::string& ugly);
void LogThis(const std::string& text) {
Prettify(text);
std::cout << "Log: " << text << '\n';
}
static void Prettify(std::string& ugly) { }
但是如果你的日志工具是面向对象的,我建议你使用D-poin。我建议你使用D-pointer和Q-pointer设计模式(也被称为PImpl习语)------。https:/en.cppreference.comwcpplanguagepimpl。 .
//logging.h
#include <string>
class loggingImpl;
class logging {
public :
logging();
virtual ~logging();
void LogThis(const std::string& text);
protected :
loggingImpl *impl;
};
//logging.cpp
class loggingImpl
{
public :
loggingImpl(logging *p) : qptr(p) { }
void Prettify(std::string& ugly) { }
//anything what you need and should be hided
// access parent through qptr
protected :
logging *qptr;
};
logging::logging() : impl(new loggingImpl) { }
logging::~logging() { delete impl; }
void logging::LogThis(const std::string& text) {
impl->Prettify(text);
std::cout << "Log: " << text << '\n';
}
正如你所写的,由于限制了未使用的符号的可见性,将声明放在头文件中是不合适的。