ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

string类、智能指针

2022-05-27 20:00:26  阅读:157  来源: 互联网

标签:string auto 智能 new unique ptr 指针


1. string 类 p531

1)构造函数

一个比较特殊的构造函数

template <class Iter>
string (Iter begin, Iter end); // 范围包括 begin 在内,但不包括 end

例如:

char c[] = "All's well that ends well";
...
string s(c+6, c+10);

注意,假设有另外一个 string 对象 s1:

string s1;
...
string s(s1+6, s1+10); // not allowed
string s(&s1[6], &s1[10]; // allowed

因为 s1 (对象名,不同于数组名)不会被看作是对象的地址,因此 s1 不是指针,所以 s1 + 6 是没有意义的;

而 s1[6] 是一个 char 字符,因此 &s1[6] 是一个地址,可以用作该构造函数的一个参数。

2)string 类的输入 p533

对于 C 风格字符串有 3 种输入方式

char info[100];
cin >> info;
cin.getline(info, 100);// read a line, discard \n
cin.get(info, 100);// read a line, leave \n in queue

对于 string 对象,有两种输入方式

string stuff;
cin >> stuff;
getline(cin, stuff);// read a line, discard queue

 string 版本的 getline() 函数从输入中读取字符,并将其存储到目标 string 中,直到发生下列三种情况之一:p534

  • 到达文件尾;这种情况下,输入流的 eofbit 将被设置,方法 fail() 和方法 eof() 都返回 true;
  • 遇到分界字符(默认为 \n);这种情况下,把分界字符从输入流中删除,但不存储它;
  • 读取字符的数量达到最大允许值(string::npos 和可供给分配的内存字节数中较小的一个),这种情况下,将设置输入流的 failbit 为1,fail() 返回 true;

string 版本的 operator>>() 函数的行为与此类似;只是它不断读取,直到遇到空白字符并将其留在输入队列中。(cin 不丢弃空白符,p68)

3)string 指定分界符

两个版本的 getline() 函数都有一个可选参数,用于指定使用哪个字符来确定输入的边界:

cin.getline(info, 100, ':'); // 读取直到遇到 :,并丢弃:
getline(stuff, ':'); // 读取直到遇到:,并丢弃:

注意,指定分界字符后,换行符 \n 将被视为常规字符。

例子:见 p534

3)使用字符串 p535

size(), length(), find()

capacity() // 返回当前分配给字符串的内存块的大小

reserve() // 请求内存块长度

 

2. 智能指针 p539

智能指针是行为类似于指针的类对象。

auto_ptr, unique_ptr, shard_ptr 都定义了类似指针的对象,可以将 new 获得(直接或间接)的地址赋给这种对象。

当智能指针过期时,其析构函数将使用 delete 来释放内存;在智能指针过期时,这些内存将自动被释放。

void remodel (std::string & str)
{
    std::string * ps = new std::string(str);
    ...
    if (weird_thing())
        throw exception();
    str = *ps;
    delete ps;
    return;
}

在上述代码中,当出现异常时,delete 将不被执行,将导致内存泄漏。

1)使用智能指针

要创建智能指针对象,必须包含头文件 memory;该文件包含模板定义,然后使用通常的模板语法来实例化所需类型的指针;

例如,模板 auto_ptr 包含如下构造函数

template<class X>
class auto_ptr {
public:
    explicit auto_ptr(X* p = 0) throw(); //throw() 表示构造函数不会引发异常

请求 X 类型的 auto_ptr 将获得一个指向 X 类型的 auto_ptr:

auto_ptr<double> pd(new double); // pd an auto_ptr to double(use in place of double * pd)
auto_ptr<string> ps(new string); // ps an auto_ptr to string(use in place of string * ps)

new double 是 new 返回的指针,指向新分配的内存块。它是构造函数 auto_ptr<double> 的参数,即对应于原型中 p 的实参。

其他两种智能指针使用同样的语法:

unique_ptr<double> pdu(new double); // pdu and unique_ptr to double
shared_ptr<string> pss(new string); //pss a shared_ptr to string

因此,可以使用智能指针来修改 remodel 函数:

#include <memory>
...
void  remodel (std::string & str)
{
    auto_ptr<string> ps (new string(str)); // new string 返回一个 new 申请的空string的地址;new string(str) 返回一个 new 申请的内容为 str 的 string 的地址
    ...
    if (weird_thing())
        throw exception();
    str = *ps;
    //delete ps; NO LONGER NEED
    return;
}

所有智能指针类都有一个 explicit 构造函数,该构造函数将指针作为参数;因此,不能自动地将指针转换为智能指针对象

shared_ptr<double> pd;
double *p_reg = new double;
pd = p_reg; // now allowed (implicit conversion)
pd = shared_ptr<double>(p_reg); // allowed (explicit conversion)

shared_ptr<double> pshared = p_reg; // not allowed (implicit conversion)
shared_ptr<double> pshared(p_reg); //allowed (explicit conversion)

注意,不能将非堆内存中地地址作为智能指针构造函数地参数

string vacation("I wandered lonely as a cloud.");
shared_ptr<string> pvac(&vacation); // NO!

pvac 过期时,程序将把 delete 运算符用作非堆内存,这是错误的。

2)智能指针的区分 p542

auto_ptr 智能指针在 c++11 中已被摒弃。

auto_ptr<string> ps (new string("I reigned lonely as a cloud."));
auto_ptr<string> vocation;
vocation = ps;

上述代码中,如果 ps 和 vocation 是常规指针,则两个指针指向同一个 string 对象,这样程序将试图删除同一个 string 对象两次。解决方法如下:

  • 定义赋值运算符,执行深复制。这样两个指针将指向不同的对象,其中一个对象是另一个对象的副本;
  • 建立所有权(ownership)概念;对于特定的对象,只有一个智能指针可以拥有它,只有拥有该对象的智能指针会删除该对象;让赋值操作转让所有权;这就是用于 auto_ptr 和 unique_ptr 的策略,但 unique_ptr 的策略更严格。
  • 跟踪引用特定对象的智能指针数,这称为引用计数(reference counting);仅当最后一个指针过期时,才调用 delete;这是 shared_ptr 采用的策略。

auto_ptr, shared_ptr, unique_ptr 的区分

 在 auto_ptr 放弃对象的所有权后,便不能再使用它来访问对象:

auto_ptr<string> s(new string("Fowl Balls"));
auto_ptr<string> q;
q = s;
cout << *s << endl;// not allowed

上面的程序中 auto_ptr s 将所有权转让给 q,因此 s 不再引用该字符串。

如果在上述程序中使用 shared_ptr 代替 auto_ptr,则程序将正常运行。在执行 q = s; 语句时,此时 shared_ptr s 和 shared_ptr q 指向同一个对象,引用计数从 1 增加到 2;在程序末尾,后声明的 q 首先调用其析构函数,该析构函数将引用计数降低到1;然后 s 调用析构函数时,将引用计数降低到 0,并释放该字符串对象所在的空间。

如果在上述程序中使用 unique_ptr,unique_ptr 与 auto_ptr 一样,也采用所有权模型;但使用 unique_ptr 时,程序不会等到运行阶段崩溃,而在编译阶段因执行下述代码而出现错误:

q = s;

探讨 auto_ptr 和 unique_ptr 之间的区别:

在下面的程序中

auto_ptr<string> p1 (new string("auto")); // #1
auto_ptr<string> p2;                     // #2
p2 = p1;                                 // #3

在语句 #3 中,p2 接管 string 对象的所有权后,p1 的所有权将被剥夺,这可以防止 p1 和 p2 的析构函数试图删除同一个对象;但如果程序随后试图使用 p1 将会出错,因为 p1 不再指向有效的数据;

而在

unique_ptr<string> p3 (new string("auto")); // #1
unique_ptr<string> p4;                     // #2
p4 = p3;                                   // #3

编译器认为 #3 非法,避免了 p3 不再指向有效数据的问题。因此 unique_ptr 比 auto_ptr 更安全;

但是有时候,将一个智能指针赋给另一个并不会留下危险的悬挂指针。

unique_ptr<string> demo(const char * s)
{
    unique_ptr<string> temp(new string(s));
    return temp;
}
...
unique_ptr<string> ps;
ps = demo("Uniquely special")

在上述程序中,demo() 返回一个临时 unique_ptr,然后该临时 unique_ptr 将其指向的对象(即 "Uniquely special" 字符串)的所有权转让给 ps,紧接着该临时 unique_ptr 被销毁。

这没有问题,因为 ps 拥有了 string 对象的所有权;此外,demo() 返回的临时 unique_ptr 很快被销毁,没有机会使用它来访问无效的数据,没有理由禁止这种赋值。

编译器的确允许这种赋值!

总之,如果程序试图将一个 unique_ptr 赋给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这样做;如果源 unique_ptr 将存在一段时间,编译器将禁止这样做:

using namespace std;
unique_ptr<string> pu1(new string("Hi ho!"));
unique_ptr<string> pu2;
pu2 = pu1;                                            // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string "Yo!"); // #2 allowed

语句 #1 将留下悬挂的 unique_ptr(pu1),这可能导致危害。语句 #2 不会留下悬挂的 unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权转让给 pu3 后就会被销毁。

unique_ptr 如何区分安全和不安全的用法?---> 它使用了 C++11 新增的移动构造函数和右值引用。

此外,unique_ptr 还有另一个优点:它有一个可用于数组的变体。

在之前提到,必须将 new 和 delete 配对使用,将 new [] 和 delete [] 配对使用;

模板 auto_ptr 使用 delete 而不是 lelete [],因此只能与 new 一起使用,而不能与 new [] 一起使用;但 unique_ptr 有使用 new [] 和 delete [] 的版本:

unique_ptr<double []> pda(new double(5)); // will use delete []

注意,使用 new 分配内存时,才能使用 auto_ptr 和 shared_ptr,使用 new [] 分配内存时,不能使用它们;不使用 new 分配内存时,不能使用 auto_ptr 或 shared_ptr;不使用 new 或 new [] 分配内存时,不能使用 unique_ptr。

 

标签:string,auto,智能,new,unique,ptr,指针
来源: https://www.cnblogs.com/San-Francisco/p/16307163.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有