41、vector、list、deque的性能初窥
int large_size = 10000000;
cout_current_time("start init vector!\t");
vector<string> svec1(large_size, "Hello");
vector<string> svec2(large_size, "Hi");
cout_current_time("end init vector!\t");
cout_current_time("start init list!\t");
list<string> slist1(large_size, "Hello");
list<string> slist2(large_size, "Hi");
cout_current_time("end init list!\t");
cout_current_time("start init deque!\t");
deque<string> sdeq1(large_size, "Hello");
deque<string> sdeq2(large_size, "Hi");
cout_current_time("end init deque!\t");
用事实说话最有说服力:
start init vector! current time : 5:5:52
end init vector! current time : 5:5:55
start init list! current time : 5:5:55
end init list! current time : 5:6:14
start init deque! current time : 5:6:14
end init deque! current time : 5:6:26
可以看出大致时间比例为3/19/12。虽然不足以佐证它们的性能差距,但vector的常用似乎有了更充分的理由。
这里使用了一个简单的时间函数大致如下:
#include <time.h>
typedef struct tm * time_type;
time_type get_current_time(void) {
time_t t;
t = time(NULL);
return localtime(&t);
}
42、容器自增长(P286)
每种实现都要求遵循以下原则:确保push_back操作高效地在vector中添加元素。从技术上来说,在原来为空的vector容器上n次调用push_back函数,从而创建拥有n个元素的vector容器,其执行时间永远不能超过n的常量倍。
43、类定义中为何不能具有自身的数据成员(P375)
因为只有当类定义体完成后才能定义类,因此类不能具有自身类型的数据成员。然而,只要类名一出现就可以认为该类已声明。因此,类的数据成员可以是指向自身类型的指针或引用:
class LinkScreen {
Screen window;
LinkScreen *next;
LinkScreen *prev;
};
44、两种引用类类型的方法(P376)
Sales_item item1; //default initialized object of type Sales_item
class Sales_item item1; //equivalent definition of item1
两种引用类类型的方法是等价的。第二种方法是从C继承而来的,在C++中仍然有效。第一种更为简练,由C++语言引入,使得类类型更容易使用。
45、为什么类的定义以分号结束(P376)
分号是必须的,因为在类定义之后可以接一个对象定义列表。定义必须以分号结束:
class Sales_item {/* … */};
class Sales_item {/* … */} accum, trans;
46、形参表和函数体处于类作用域中,函数返回类型不一定在类作用域中
在定义于类外部的成员函数中,形参表和成员函数体都出现在成员名之后。这些都是在类作用域中定义,所以可以不用限定而引用其他成员。因为形参表是在Screen类作用域内,所以不必知名我们想要的是Screen::index。
如果返回类型使用由类定义的类型,则必须使用完全限定名。
#include "stdafx.h"
#include <iostream>
class MyClass
{
public :
typedef int index_t;
index_t twice(index_t in);
};
MyClass::index_t MyClass ::twice(index_t in)
{
return in * 2;
}
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
MyClass obj;
MyClass::index_t x, y;
x = 10;
y = obj.twice(x);
cout<<"x = "<<x<<"; y = "<<y<<";"<<endl;
return 0;
}
47、构造函数初始化式(P387)
与任意的成员函数一样,构造函数可以定义在类的内部或外部。构造函数初始化式只在构造函数的定义中而不是声明中指定。
构造函数初始化列表难以理解的一个原因在于,省略初始化列表并在构造函数的函数体内对数据成员赋值是合法的。
在构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化。运行该类型的默认构造函数,来初始化类类型的数据成员。内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为0。
如果那个类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。在这种情况下,为了初始化数据成员,必须提供初始化式。
对于这样的成员,在构造函数函数体中对它们赋值不起作用。没有默认构造函数的类类型成员,以及const或引用类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。
因为内置类型的成员不进行隐式初始化,所以对这些成员是进行初始化还是赋值似乎都无关紧要。除了两个例外,对非类类型的数据成员进行赋值或使用初始化式在结果和性能上都是等价的。
48、成员初始化的次序
构造函数初始化列表仅指定用于初始化成员的值,并不指定这些初始化执行的次序。成员被初始化的次序就是定义成员的次序。
class X{
int i;
int j;
public:
//run-time error: i is initialized before j
X(int val): j(val), i(j) {}
}
在这种情况下,构造函数初始化列表看起来似乎是用val初始化j,然后再用j来初始化i。然而i首先被初始化。这个初始化列表的效果是用尚未初始化的j值来初始化i!
49、使用默认构造函数(P393)
常犯的一个错误是采用以下方式声明一个用默认构造函数初始化的对象:
Sales_item myobj();
Sales_item myobj(); //ok: but defines a function, not an object
if(myobj.same_isbn(Primer_3rd_ed)) // error: myobj is a function
正确的方式应该是去掉相应的括号:
Sales_item myobj;
或者
Sales_item myobj = Sales_item();
50、显式或隐式初始化
#include "stdafx.h"
#include <iostream>
using namespace std;
class MyClass
{
public :
typedef int index_t;
bool same_object(MyClass obj);
public :
MyClass(int default_index = 5)
:default_index(default_index),
m_name("default_name"){}
MyClass::MyClass(std::string name);
public :
int default_index;
std::string m_name;
};
MyClass::MyClass(std::string name)
:default_index(0), m_name(name){}
bool MyClass::same_object(MyClass obj)
{
cout<<"m_name = "<<m_name.c_str()<<endl;
cout<<"obj.m_name = "<<obj.m_name.c_str()<<endl;
return strcmp(obj.m_name.c_str(), m_name.c_str()) == 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
MyClass obj;
cout<<"explicit : "<<obj.same_object(MyClass("default_name"))<<endl;
cout<<"implicit : "<<obj.same_object(string("default_name"))<<endl;
return 0;
}
因为具有以std::string为形参的构造函数,因此在调用需要MyClass对象的same_object成员函数时,会自动隐式调用该构造函数构建MyClass对象,用于操作。但生成的MyClass对象是临时对象,在same_object函数调用完成后销毁。如果为了避免产生隐式转换可以使用explicit关键字来抑制由构造函数定义的隐式转换: