第三篇 模板
模板只不过是语法宏的一种受限形式,为什么模板这么重要?
“实用性”,与普通的宏不同,C++模板是足够安全的,可以完全放心的使用。另外,它们卸掉了许多原本压在程序员肩头的重担,使得他们乐意使用。与宏很接近,因此,生成的代码运行起来和完全用手工写出来的代码速度一样快。
另一种看法:编译期函数——以类型作为参数并产生代码。从某种意义上说,模板使抽象与编写程序相关的类型细节成为可能。
模板的传统用法:容器类。(12章到15章)。
第十二章:设计容器类
容器是一种保存值的集合的数据结构。C有两种内建的容器:数组和结构体。
设计一个成功的容器不可能是自然而然的:如果有唯一正确的方法来做这件事情,那么倒不如把它内建到语言中罢了。
12-1 包含什么
包含对象的确切含义?c1,c2是容器,obj是对象。容器应该有insert函数,mutate函数能改变它的值。
c1.insert(obj);
obj.mutate();
上面的代码会改变c1中对象的值吗?我们插入的只是obj的一个副本吗?
c1.insert(obj);
c2.insert(obj);
改变其中一个容器内的该对象的值,会不会改变另一个?
void f()
{
Container c;
{
Object obj;
c.insert(obj);
}
//C现在还有效吗
}
在这段代码中创建了一个容器,并插入一个对象,接着又删除了这个对象。如果是对象本身在容器中,而不是对象的副本,那么容器怎么能够知道对象已经不存在了呢?可行办法:声明“不存在“,并且要求容器的使用者必须确保至少容器存在的时候对象还存在。如果这就是我们想要的行为,那么我们为什么不直接把指向对象的指针而不是对象本身放入容器中,这样无疑简单了很多?
充分的理由建议:容器应该包含放在其中的对象的副本,而不是原对象本身。想在多个容器中包含同一个对象的用户——可以把指向该对象的指针放入到容器中。这个策略的好处:用户不必学习一套新的语义;因为他们通常都已经知道如何处理指针了。
12-2 复制容器意味着什么?
复制容器是不是也应该复制包含在容器中的对象呢?
Container c1;
Container c2(c1);
或者
Container c2;
c2 =c1;
改变c1中的对象会不会影响c2。
c1 = c2;
// 向c2中添加一个新值
c2.insert(some_new_obj);
//改变c2中已有的值
c2.update(some_existing_obj, some_new_value);
上述这些操作如果对c1有影响,那么会是怎样的影响呢?
如果复制c2和c1中会导致c2和c1指向同一个底层对象,那么c2的改变也会影射到c1中。另一方面,如果我们定义复制意味着把c2的值放入c1中,则这些对c2的改变不会影响到c1了。
了解c和c++如何处理相关内建类型可能是有用的。
struct C {
int i;
double d;
} c1, c2;
/*... */
c1 = c2;
就意味着保存在c2.i 和 c2.d中的值被复制到了相应的c1的成员中。如果c2.i = /* 某新值 */
则cl.i保持不变。
对于C数组:数组不允许赋值操作。
int x[N];
int y[N] = x; /* 不合法 */
int y[N];
y = x; /* 不合法 */
void f(int [N]);
int x[N];
void g()
{
f(x);
}
这里,我们希望值调用语义可以将x的副本传递给f。实际的结果是f被有效地转型为对int*类型的操作,对f的调用也被转换成向f传递x的第一个元素的地址了。
void f(int*);
int x[N];
vid g()
{
f(&x[0];
}
因此:C 和C++的内建集合都实现了两种不同的方法。
结构体实现值语义
数组实现引用语义
从效率上讲:复制容器只不过是简单地使两个容器指向同一个底层对象,效率就会提高,下面编程时常见错误。
void f(Container);
而用:
void f(const Container&);
如果我们想避免粗心地复制大对象,可以把复制构造函数设为private。
如果用户想传递容器的可潜在修改的引用给一个函数,那么可以这两种方法:
void f(Container&);
void f(Container*);
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/409557/viewspace-892601/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/409557/viewspace-892601/