[C/C++] - Tips for STL and Generic Programming
原文地址:
http://tmh-coding-tips.blogspot.com/2008/09/cc-tips-for-stl-and-generic-programming.html
[C/C++] - Tips for STL and Generic Programming
关于STL与泛型编程的忠告(v1.0)
Translated By Phoenix(E-Mail:phoenix8848@gmail.com)
Tip 15: The Location of Template Definitions
忠告15:模板定义的位置
Normally, you declare functions and classes in a .h file and place their definition in a separate .cpp file. With templates, this practice isn't really useful because the compiler must see the actual definition (i.e., the body) of a template, not just its declaration, when it instantiates a template. Therefore, it's best to place both the template's declaration and definition in the same .h file. This is why all STL header files contain template definitions.
通常你在.h文件中声明函数与类而把它们的实现放在若干个.cpp文件中(以使声明与实现分开)。使用模板的时候这样的做法作用不大因为编译器在实例化一个类模板时必须显式地获得类模板的实际定义(例如函数体),而不仅仅是它的声明。所以最好是把类模板的声明和实现放在同一个头文件中。这就是为什么所有的STL(标准模板库,即Standard Template Library,是一个C++软件库,也是C++标准程式库的一部分)头文件都包含模板的实现。
In the future, when compilers support the "export" keyword, it will be possible to use only the template's declaration and leave the definition in a separate source file.
将来当编译器支持"export"命令时,有可能只需要模板的声明而将它的实现放在单独的源文件中。
Tip 16: Standard Base Classes for Function Object
忠告16:函数对象的标准基类
To simplify the process of writing function objects, the Standard Library provides two class templates that serve as base classes of user-defined function objects: std::unary_function and std::binary_function. Both are declared in the header "functional". As the names suggest, unary_function serves as a base class of function objects taking one argument and binary_function serves as a base class of function objects taking two arguments. The definitions of these base classes are as follows:
为了简便地处理写入函数对象,标准库提供了两个类模板以作为用户自定义函数对象的基类:std::unary_function和std::binary_funcation。这两个模板都声明在头文件"functional"中。正如模板名字所示,unary_funcation作为单参数函数对象的基类而binary_function作为双参数函数对象的基类。这两个基类的定义如下所示:
template <class Arg, class Res>
struct unary_function
{
typedef Arg argument_type;
typedef Res result_type;
};
template <class Arg, class Arg2, class Res>
struct binary_function
{
typedef Arg first_argument_type;
typedef Arg2 second_argument_type;
typedef Res result_type;
};
These templates don't provide any useful functionality. They merely ensure that arguments and return values of their derived function objects have uniform names. In the following example, the predicate is_vowel, which takes one argument, inherits from unary_function:
这两个模板并没有提供任何有用的功能。它们仅仅是为了保证派生出来的函数对象的参数与返回值具有不同的名字。在下面的例子中声明了从unary_function派生的_vowel,它只有一个参数。
template <class T>
class is_vowel: public unary_function<T, bool>
{
public:
bool operator ()(T t) const
{
if ((t=='a')||(t=='e')||(t=='i')||(t=='o')||(t=='u'))
return true;
else
return false;
}
};
Tip 17: Storing Dynamically Allocated Objects in STL Containers
忠告17:在标准模板库容器中保存动态生成的对象
Suppose you need to store objects of different types in the same container. Usually, you do this by storing pointers to dynamically allocated objects. However, instead of using named pointers, insert the elements to the container as follows:
假设你需要在一个容器中存储不同的对象。通常你会通过保存动态生成对象的指针来实现。无论如何不要用命名的指针,而是像下面所示的这样添加元素到容器中:
class Base {};
class Derived : public Base{};
std::vector<Base *> v;
v.push_back(new Derived);
v.push_back(new Base);
This way you ensure that the stored objects can only be accessed through their container. Remember to delete the allocated objects as follows:
这样你可以保证存储的对象只能通过容器访问到。切记要用下面所示的方法释放对象:
delete v[0];
delete v[1];
Tip 18: Treating a Vector as an Array
忠告18:将向量容器看作是数组
Suppose you have a vector of int and function that takes int *. To obtain the address of the internal array of the vector v and pass it to the function, use the expressions &v[0] or &*v.front(). For example:
假设你有一个保存整型变量的向量容器和一个处理整型指针int*的函数。为了获得向量容器v中的内部数组并把它传递给函数,应该使用表达式&v[0]或者&*v.front(),如示例:
void func(const int arr[], size_t length );
int main()
{
vector<int> vi;
//.. fill vi
func(&vi[0], vi.size());
}
It's safe to use &vi[0] and &*v.front() as the internal array's address as long as you adhere to the following rules: First, func() shouldn't access out-of-range array elements. Second, the elements inside the vector must be contiguous. Although the C++ Standard doesn't guarantee that yet, I'm not aware of any implementation that doesn't use contiguous memory for vectors. Furthermore, this loophole in the C++ Standard will be fixed soon.
如果你能遵循下面的规则使用&vi[0]和&*v.front()作为内部数组的地址是安全的:第一,func()函数不能试图访问超出范围的数组元素。第二,向量容器内的元素必须是连续的。尽管C++标准并不要求如此,但我不知道有别的为容器实现不使用连续内存。而且,C++标准不久就会修补这个漏洞。
Tip 19: Dynamic Multidimensional Arrays and Vectors
忠告19:动态多维数组与容器
You can allocate multidimensional arrays manually, as in:
通常你会像下面的方式来分配多维数据:
int (*ppi) [5]=new int[4][5]; /*parentheses required*/
/*fill array..*/
ppi[0][0] = 65;
ppi[0][1] = 66;
ppi[0][2] = 67;
//..
delete [] ppi;
However, this style is tedious and error prone. You must parenthesize ppi to ensure that the compiler parses the declaration correctly, and you must delete the allocated memory. Worse yet, you can easily bump into buffer overflows. Using a vector of vectors to simulate a multidimensional array is a significantly superior alternative:
可是这种做法是冗长而且非常容易出错。你必须把ppi放入括号内以便编译器可以正确地分析这些声明,并且(最后)你必须(显式地)地释放这些分配的内存空间。更糟糕的是,你会很容易地造成缓冲溢出。使用"容器之容器"来模拟多维数组是一种高明得多的选择。
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<vector<int>> v; /*two dimensions*/
v.push_back(vector<int>()); /*create v[0]*/
v.push_back(vector<int>()); /*create v[1]*/
v[0].push_back(15); /*assign v[0][0]*/
v[1].push_back(16); /*assign v[1][0]*/
}
Because vector overloads operator [], you can use the [][] notation as if you were using a built-in two-dimensional array:
因为容器重载了操作符[],所以你可以用[][]来操作就如同使用内置的二维数组。
cout<<v[0][0];
cout<<v[1][0];
The main advantages of using a vector of vectors are two: vector automatically allocates memory as needed. Secondly, it takes care of deallocating memory so you don't have to worry about potential memory leaks.
使用"容器之容器"有两个主要的好处:第一,容器会在需要时自动分配内存。第二,容器可以自动回收内存所以你不必操心潜在的内存泄漏。
Tip 20: Why You Shouldn't Store auto_ptr Objects in STL Containers
忠告20:为什么不能在标准模板库容器中存储auto_ptr
The C++ Standard says that an STL element must be "copy-constructible" and "assignable." These fancy terms basically mean that for a given class, assigning and copying one object to another are well-behaved operations. In particular, the state of the original object isn't changed when you copy it to the target object.
C++标准中说一个STL元素必须是可复制构造(copy-constructible)和可赋值(assignable)的。这些充满科幻色彩的词汇的基本意思是对于一个给定的类,对象间的赋值与复制是一种表现良好的操作。特别的,源对象被复制给一个目标对象时源对象不会有任何改变。
This is not the case with auto_ptr, though: copying or assigning one auto_ptr to another makes changes to the original in addition to the expected changes in the copy. To be more specific, the original object transfers ownership of the pointer to the target, thus making the pointer in the original null. Imagine what would happen if you did something like this:
这不适用于auto_ptr。因为,把一个auto_ptr对象复制或赋值给另一个对象会引起原始对象的改变从而引起对象副本的改变。更细节地看,原始对象把指针的权限传递给了目标对象,这使源对象中的指针变成空指针。设想当你进行下面这样操作的时候会发生什么:
std::vector<auto_ptr<Foo>> vf;/*a vector of auto_ptr's*/
// ..fill vf
int g()
{
std::auto_ptr<Foo> temp=vf[0]; /*vf[0] becomes null*/
}
When temp is initialized, the pointer of vf[0] becomes null. Any attempt to use that element will cause a runtime crash. This situation is likely to occur whenever you copy an element from the container. Remember that even if your code doesn't perform any explicit copy or assignment operations, many algorithms (std::swap(), std::random_shuffle() etc.) create a temporary copy of one or more container elements. Furthermore, certain member functions of the container create a temporary copy of one or more elements, thereby nullifying them. Any subsequent attempt to the container elements is therefore undefined.
当临时对象被初始化,vf[0]的指针变为空。任何试图使用这个元素都会引起运行时崩溃。无论什么时候你从容器复制这个元素都会引发这种意外。切记即使你的代码不进行任何显示的复制与赋值操作,很多算法(如std::swap(), std::random_shuffle())都会(隐蔽地)创建一个或更多容器元素的临时副本。此外,容器的某些特定成员函数也会一个或列多元素的临时副本,从而使这些元素失效。任何随后的对容器元素的操作意图都由此变为不可预期的。
Visual C++ users often say that they have never encountered any problems with using auto_ptr in STL containers. This is because the auto_ptr implementation of Visual C++ (all versions thereof) is outdated and relies on an obsolete specification. When the vendor decides to catch up with the current ANSI/ISO C++ Standard and change its Standard Library accordingly, code that uses auto_ptr in STL containers will manifest serious malfunctions.
Visual C++用户经常说他们在标准模板容器中使用auto_ptr时从没遇到什么问题。这是因为Visual C++中auto_ptr的实现(目前为止所有的版本)都是过时的和参考了旧的规范说明。当微软决定采用现在的ANSI/ISO C++标准并据此对它的标准库进行更新时,以前在标准模板容器中使用auto_ptr的代码就会突现严重的问题。
To conclude, you shouldn't use auto_ptr in STL containers. Use either bare pointers or other smart pointer classes instead of auto_ptr (such classes are available at www.Boost.org).
作为结论,你最好不要在标准容器中使用auto_ptr,而是使用原始指针或别的智能指针类来代替auto_ptr(比如BOOST库)
posted on 2009-05-26 02:08 phoenix8848cn 阅读(256) 评论(0) 编辑 收藏 举报