我作为一个C++初学者,想从一开始就练习编写可移植的代码,有哪些需要注意的事项?
谢谢。
我作为一个C++初学者,想从一开始就练习编写可移植的代码,有哪些需要注意的事项?
谢谢。
shared_ptr
,除非已经有内部替代方案,否则应该成为事实上的标准。无论如何,您为什么认为它不适合小项目?只需包括您要使用的内容,如果需要分发源代码,则只需打包相关部分即可。 - Georg Fritzsche请将特定平台的代码与可重用代码分离,最好放在不同的文件中,但至少应该放在不同的函数中。如果你到处都有 #if WIN32
、#if CYGWIN
和 #if BSD
,那么维护就成了噩梦。
然后,请尽早并经常在至少两个不同的平台上进行编译。典型选择是Windows上的Visual C++和Linux上的gcc。由于系统库和编译器都不共享,因此您会在非可移植代码深入设计之前就捕捉到它们。
尽可能使用STL类型。注意不要使用依赖于系统的类型和API。例如,在Windows上不要使用UINT和DWORD等类型。
您可以使用像boost这样的库来使编写可移植代码更容易。如果需要GUI,请考虑使用跨平台工具包,如Qt。
有时您需要编写特定于平台的代码,在这种情况下,您可以这样做:
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
虽然已经有人提到过这个问题,但以下是我的看法:
1)您需要使用C++吗?由于它接近底层,所以不是写可移植代码的最佳语言。Java、Python、Perl、PHP或Javascript可能更适合您。
2)如果您需要使用C++,不要试图编写完全可移植的代码,因为这几乎是不可能的。相反,请尽早决定要支持哪些平台。例如:Linux、MacOS X、Windows。
3)确保您在所有选择的平台上持续测试代码。不要只在Windows上构建并期望“完成后”仅编译Linux版本。每天在所有平台上编译,并确保您持续测试以查找问题。
不谨慎的程序员很可能会陷入许多陷阱中,我们可以试图对其进行分类。但首先要告诉你的是:绝对不可能。
问题在于,即使符合标准的代码也可能因为特定编译器问题而无法移植。
现在我能想到的主要类别如下:
编译器扩展
比如使用变量数组:
void func(int const n)
{
int array[n];
}
这不是标准做法,但许多编译器仍然支持它,因为它很实用。
标准库扩展
许多标准库实现提供了一个从未被指定的std::hash_map
。如果您在代码中使用它,则无法移植。
现代趋势是将这些内容存储到std::tr1
命名空间中,以便程序员知道这是一种扩展。
还要注意,许多定义了非通用的typedef
或宏(例如PRETTY_FUNCTION
)。标准没有指定任何宏,而且很少有typedef
。
平台特定
例如,int
或double
的大小和对齐方式在标准中没有指定。如果您进行位操作并期望它具有32位,则在64位平台上,即使不更改编译器,也会出现问题。
平台API
我们的程序旨在被编译,并且通常旨在与运行它们的计算机交互:
您需要找到跨平台的可移植API,或自己编写。检查下面列表中的一些库。
库
大多数编写良好的库在很大程度上都是可移植的,只需确保它们支持:
好的库涉及以下内容:
其他库需要您进行评估...这需要时间。
我认为那里没有完美的答案。但是,由于完全可移植性是不可能的,因此您需要决定要支持哪些编译器和平台。
对于平台,您应该从Windows和一个Linux版本开始。对于编译器,请选择任意两个(如果可以负担得起,则使用Comeau)。
在C++中编写与操作系统无关的代码非常困难。看看这个简单的例子:
#include <iostream>
int main(int argc, char** argv) {
std::cout << argv[0] << std::endl;
}
这是完全有效的C++代码,但它不可移植,因为在Windows上它不会接受Unicode命令行参数。在Windows上正确的版本应该是:
#include <iostream>
int wmain(int argc, wchar_t** argv) {
std::wcout << argv[0] << std::endl;
}
main()
函数,也必须使用条件编译。std::endl
代替,它会刷新缓冲区并实际产生一些输出。我无法计算所有问题的数量,这些问题归结为人们没有刷新cout
。 - Ben Voigtwchar_t
始终是一个16位无符号整数(否则没有程序会编译通过),所以如果你想在Windows上支持Unicode,可以使用wchar_t
。
@Ben:谢谢,我会进行更正。 - Philipp一些指导方针:
有时为了获得可移植性,你需要在效率和性能之间进行权衡。例如,如果你的代码需要访问缓冲区中的字段,你可以将紧凑的结构体强制转换为缓冲区指针。但那非常不可移植。所以,你需要使用通过偏移计算出来的命名指针--有时还要处理边界对齐处理代码。虽然不美观,但却是可移植的。幸运的是,你可以通过巧妙使用类接口来隐藏大部分这些内容。
并非所有代码都需要按照这种方式编写。如果你以非常模块化的方式设计应用程序,并定义明确的责任范围,那么90-95%的代码就可以在不痛苦的情况下实现可移植性。然后只需将5-10%的代码隔离在一个非常局部化的区域中,以便为新平台进行定制。
在学习过程中,尽量避免只关注一种实现的书籍。有些书的导言或早期章节会提供有关如何获得或使用语言实现的说明;如果它提到了多个实现,那么你可能没问题。
获取一个独立于平台的参考书。 Stroustrup 的《C++ 程序设计语言》是一本很好的参考书,但对于初学者来说并不适合当作入门书籍。不要依赖于特定实现的参考书。例如 MSDN 很有用,但它的主要关注点是如何使用 Visual C++ 编写 Windows 程序,而不是如何编写能够在任何地方编译和运行的程序。
为了编写任何真正有用的东西,你需要进入非可移植代码领域。试着养成将用户界面代码与其他所有代码分开的习惯,因为这是你的兼容性最小的地方。你要改变的代码越少,你的代码就越具有可移植性。