一、string类
1、构造函数
string实际上是basic_string<char>的一个typedef,同时省略了与内存管理相关的参数。size_type是一个依赖于实现的整型,是在string中定义的。string类将string::npos定义为字符串的最大长度,通常为unsigned int的最大值。另外,使用缩写NBTS(null-terminated string)来表示以空字符结束的字符串——传统的C字符串。下面的表格中列出了string的构造函数:
构造函数 | 描述 |
string(const char * s) | 将string对象初始化为s指向的NBTS |
string(size_type n, char c) | 创建一个包含n个元素的string对象,其中每个元素都被初始化为字符c |
string(const string & str) | 将一个string对象初始化为string对象str(复制构造函数) |
string() | 创建一个默认的string对象,长度为0(默认构造函数) |
string(const char * s,size_type n) | 将string对象初始化为s指向的NBTS的前n个字符,即使超过了NBTS结尾 |
template<class Iter> string(Iter begin,Iter end) |
将string对象初始化为区间[begin,end)内的字符,其中begin和end的行为就像指针,用于指定位置,范围包括begin在内,但不包括end |
string(const string & str, size_type pos = 0,size_type n = npos) | 将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符 |
string(string && str)noexcept | 这是C++11新增的,他将一个string对象初始化为string对象str,并可能修改str(移动构造函数) |
string(initializer_list<char> il) | 这是C++11新增的,它将一个string对象初始化为初始化列表il中的字符 |
2、string类输入
对于类,需要知道有哪些输入方式可用。对于C风格字符串,有3种方式:
char info[100];
cin >> info;//读取一个词
cin.getline(info,100);//读取一行,遇到\n结束,并丢弃\n
cin.get(info, 100);//读取一行,遇到\n结束,将\n丢弃在输入队列中
对于string对象,有两种方式:
string stuff;
cin >> stuff;//读取一个词
getline(cin,stuff);//读取一行,遇到\n结束并丢弃\n
二、智能指针模板类
智能指针是行为类似于指针的类对象。
auto_ptr是C++98提供的解决方案,C++11已将其摒弃,并提供了另外两种解决方案。然而,虽然auto_ptr被摒弃,但它已经使用了多年;同时,如果编译器不支持其他两种解决方案,auto_ptr将是唯一的解决方案。
2.1、使用智能指针
auto_ptr、unique_ptr和shared_ptr这三个智能指针模板都定义了类似指针的对象,可以将new获得(直接或间接)的地址赋给这种对象。当智能指针过期,其析构函数将使用delete来释放内存。因此,如果将new返回的地址赋给这些对象,将无需记住稍后释放这些内存:在智能指针过期时,这些能存将自动被释放。
要创建智能指针对象,必须包含头文件memory,该文件模板定义。然后使用通常的模板语法来实例化所需类型的指针。例如,模板auto_ptr包含如下构造函数:
template<class X> class auto_ptr{
public:
explicit auto_ptr(X *p = 0) throw();
...};
throw()意味着构造函数不会引发异常;与auto_ptr一样,throw()也被摒弃。因此,请求X类型的auto_ptr将获得一个指向X类型的auto_ptr:
auto_ptr<double> pd(new double);
auto_ptr<string> ps(new string);
new double是new返回的指针,指向新分配的内存块。它是构造函数auto_ptr<double>的参数,即对应于原型中参数p的实参。同样,new string也是构造函数的实参。其他两种智能指针使用同样的语法:
unique_ptr<double> pdu(new double);
shared_ptr<string> pss(new string);
智能指针模板位于名称空间std中。下面的程序演示了如何使用全部三种智能指针。每个智能指针都放在同一个代码块内,这样离开代码的时候,指针将过期。Report类使用方法报告对象的创建和销毁。
1 #include <iostream> 2 #include <memory> 3 4 class Report{ 5 private: 6 std::string str; 7 public: 8 Report(const std::string & s):str(s){ 9 std::cout << "创建了对象" << str <<"! \n"; 10 } 11 ~Report(){ 12 std::cout << "销毁了对象" << str << "! \n"; 13 } 14 void comment()const{ 15 std::cout << str << "\n"; 16 } 17 }; 18 19 int main(int argc, char * argv[]) 20 { 21 using namespace std; 22 { 23 auto_ptr<Report> ps(new Report("using auto_ptr")); 24 ps->comment(); 25 } 26 { 27 shared_ptr<Report> ps(new Report("using shared_ptr")); 28 ps->comment(); 29 } 30 { 31 unique_ptr<Report> ps(new Report("using unique_ptr")); 32 ps->comment(); 33 } 34 return 0; 35 } 36 37 输出结果: 38 创建了对象using auto_ptr! 39 using auto_ptr 40 销毁了对象using auto_ptr! 41 创建了对象using shared_ptr! 42 using shared_ptr 43 销毁了对象using shared_ptr! 44 创建了对象using unique_ptr! 45 using unique_ptr 46 销毁了对象using unique_ptr!
所有智能指针都有一个explicit构造函数,该构造函数将指针作为参数。因此不需要自动将指针转换为智能指针对象。
由于智能指针模板定义方式,智能指针对象的很多方面都类似于常规指针。
2.2、有关智能指针的注意事项
为何使用三种智能指针呢?实际上有4种,另外一种是weak_ptr,但我们不讨论它。为何摒弃auto_ptr呢?
先来看下面的复制语句:
std::auto_ptr<std::string> ps(new std::string("Good morning!"));
std::auto_ptr<std::string> vocation;
vocation = ps;
上述赋值语句将完成什么工作呢?如果ps和vocation是常规指针,则两个指针将指向同一个string对象。这是不能接受的,因为程序将视图删除同一个对象两次——一次是ps过期时,另一次是vocation过期时。要避免这种问题,方法有多种。
*定义赋值运算符,使之执行深度复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本。
*建立所有权(ownership)概念,对于特定的值,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后,让赋值操作转让所有权。这就是用于auto_ptr和unique_ptr的策略,但unique_ptr的策略更严格。
*创建智能更高的指针,跟踪引用特定对象的智能指针数。这称为引用计数(reference counting)。例如,赋值时,计数将加1,而指针过期时,计数将减1 。仅当最后一个指针过期时,才调用delete。这是shared_ptr采用的策略。
当然同样的策略也适用于复制构造函数。
每种方法都有用途。下面的程序清单是一个不适合auto_ptr的示例:
#include <iostream> #include <memory> #include <string> int main(int argc, char * argv[]) { using namespace std; auto_ptr<string> films[5] = { auto_ptr<string> (new string("卡卡西")), auto_ptr<string> (new string("佐助")), auto_ptr<string> (new string("宇智波鼬")), auto_ptr<string> (new string("凯")), auto_ptr<string> (new string("宁次")) }; auto_ptr<string> pwin; pwin = films[2]; for (int i = 0; i < 5; i++) { cout << *films[i] << endl; } cout << *pwin << endl; cin.get(); return 0; } 输出结果: 卡卡西 佐助 (lldb) //程序崩溃了
程序崩溃的原因在于,下面的语句将所有权从film[2]转让给pwin:
pwin = films[2]; //films[2]丢失所有权
这导致films[2]不再引用该字符串。在auto_ptr放弃对象的所有权后,便可能使用它来反问该对象。当程序打印films[2]指向的字符串时,却发现这是一个空指针。
如果在上面的程序清单中用shared_ptr代替auto_ptr,则程序运行正常:
1 #include <iostream> 2 #include <memory> 3 #include <string> 4 5 int main(int argc, char * argv[]) 6 { 7 using namespace std; 8 shared_ptr<string> films[5] = { 9 auto_ptr<string> (new string("卡卡西")), 10 auto_ptr<string> (new string("佐助")), 11 auto_ptr<string> (new string("宇智波鼬")), 12 auto_ptr<string> (new string("凯")), 13 auto_ptr<string> (new string("宁次")) 14 }; 15 shared_ptr<string> pwin; 16 pwin = films[2]; 17 18 for (int i = 0; i < 5; i++) { 19 cout << *films[i] << endl; 20 } 21 cout << *pwin << endl; 22 cin.get(); 23 return 0; 24 } 25 26 输出结果: 27 卡卡西 28 佐助 29 宇智波鼬 30 凯 31 宁次 32 宇智波鼬
这次pwin和films[2]指向同一个对象,而引用计数从1增加到2 。在程序末尾,后声明的pwin首先调用其析构函数,该析构函数将引用计数降低到1 。然后,shared_ptr数组的成员被释放,对films[2]调用析构函数时,将引用计数降低到0,并释放以前分配的空间。
unique_ptr与auto_ptr一样,也采用所有权模型。但使用unique_ptr时,程序不会等到运行阶段崩溃,而在编译阶段出现错误。