您的位置:首页 > 编程语言 > C语言/C++

C++标准库学习笔记(Shared Pointer)-3

2015-06-16 22:59 519 查看
声明:这个博文所有内容均来自于C++标准库-自学教程与参考手册(第二版)英文版 上册。如果转载,务必附带本声明,并注明出处。

smart pointer就是智能指针,它能够自动计数引用,并在最后一次引用后释放,包含于< memory>头文件。C++标准库-自学教程与参考手册(第二版)英文版Page76指出:

Since C, we know that pointers are important but are a source of trouble. One reason to use pointers is to have reference semantics outside the usual boundaries of scope. However, it can be very tricky to ensure that their lifetime and the lifetime of the objects they refer to match, especially when multiple pointers refer to the same object. For example, to have the same object in multiple collections (see Chapter 7), you have to pass a pointer into each collection, and ideally there should be no

problems when one of the pointers gets destroyed (no “dangling pointers” or multiple deletions of the referenced object) and when the last reference to an object gets destroyed (no “resource leaks”).

A usual approach to avoid these kinds of problems is to use “smart pointers.” They are “smart”in the sense that they support programmers in avoiding problems such as those just described. For example, a smart pointer can be so smart that it “knows” whether it is the last pointer to an object and uses this knowledge to delete an associated object only when it, as “last owner” of an object, gets destroyed. Note, however, that it is not sufficient to provide only one smart pointer class. Smart pointers can be smart about different aspects and might fulfill different priorities, because you might pay a price for the smartness. Note that with a specific smart pointer, it’s still possible to misuse a pointer or to program erroneous behavior.

自从C++11以后,C++标准库提供两种类型的 smart pointer: 一个是shared_ptr. 它使得多个smart pointer可以指向同一个object. 另一个是unique_ptr。 它是一种互斥的指针,使得一个object仅有一个smart pointer可以指向它。

include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;
int main()
{
    // two shared pointers representing two persons by their name
    shared_ptr<string> pNico(new string("nico"));  //这里使用了隐式转换,将普通 string 指针转换为了shared_ptr<string>
    shared_ptr<string> pJutta(new string("jutta"));
    // capitalize person names
    (*pNico)[0] = 'N';
    pJutta->replace(0, 1, "J");
    // put them multiple times in a container
    vector<shared_ptr<string>> whoMadeCoffee;
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pNico);
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pNico);
    // print all elements
    for (auto ptr : whoMadeCoffee) {
        cout << *ptr << " ";
    }
    cout << endl;
    // overwrite a name again
    *pNico = "Nicolai";
    // print all elements again
    for (auto ptr : whoMadeCoffee) {
        cout << *ptr << " ";
    }
    cout << endl;
    // print some internal data
    cout << "use_count: " << whoMadeCoffee[0].use_count() << endl;
    cout << typeid(pJutta).name() << endl;
    cout << pJutta << endl;
    cout << *pJutta << endl;

    shared_ptr<string> mSharedPtr;
    mSharedPtr = static_cast<shared_ptr<string> > (new string("Hello world")); //这里使用了static_cast施行转换了
    cout << *mSharedPtr << endl;
    mSharedPtr.reset(new string("Hello world's change"));
    cout << *mSharedPtr << endl;

    shared_ptr<string> mSharedPtr2 = make_shared<string>("您好,这里放的是密码。");
    cout << *mSharedPtr2 << endl;

    shared_ptr<string> mSharedPtr3(new string("my shared pointer 3th"), [](string*p){
        cout << "delete " << *p << endl;
        delete p;
    });
    cout << "Now set mSharedPtr3 as nullptr" << endl;
    mSharedPtr3 = nullptr;
    cout << "Now call resize of whoMadeCoffee" << endl;
    whoMadeCoffee.resize(2);
    //可以看到,这里的mSharedPtr3所指向的对象在它指向别的地方时释放了,而且释放的时候调用了自定义的释放函数,其实是一个lambda表达式

    shared_ptr<string> mSharedPtr4(new string("my shared pointer 3th"));
    return 0;
}


查阅C++ reference后,里面也给出了一个利用lambda显示释放的例子。不过我希望调用函数来释放,怎么办?

// shared_ptr destructor example
#include <iostream>
#include <memory>

void deleter2(int * p)
{
    std::cout << "Now delete shared_ptr in function " << *p << std::endl;
    delete p;
}
int main () {
  auto deleter = [](int*p){
    std::cout << "[deleter called]\n"; delete p;
  };

  std::shared_ptr<int> foo (new int,deleter);
  std::shared_ptr<int> foo2 (new int,deleter2);

  std::cout << "use_count: " << foo.use_count() << '\n';

  return 0;                        // [deleter called]
}


可见,用函数和用lambda都是可以的。

这里推荐一个网页运行C++的网站:http://cpp.sh/

C++标准库一书中指出,当使用shared_ptr指向一个数组时,需要自己写释放函数(尽量用lambda 吧),虽然不自己写也不一定报错,但是实际是错误的。

std::shared_ptr<int> p(new int[10],
[](int* p) {
delete[] p;
});


shared_ptr是不能直接索引下标的,它并没有重载这个符号,如果你一定要这么索引,那只能自己重载了。

T& operator[] (int idx) {
    return ptr_.get()[idx];
};


如果不想搞这么麻烦,可以使用 p.get()[index]或者(&*p)[index],参考这里

——————————————————补充———————————————————

shared pointer 的误用

第一个误用就是循环引用的问题了。循环引用会导致资源得不到释放。

第二个就是多个 shared_ptr 指向一个对象,这样可能导致第一个shared_ptr释放对象后,剩下的都找不到对象而造成错误。

比如:

int* p = new int{};
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);//错误!


所以,不能直接将两个shared_ptr直接指向一个对象,而应该这么做:

int* p = new int{};
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(sp1); //OK


这样就没问题了,两个 shared_ptr形成了一个group。

/* The following code example is taken from the book
 * "The C++ Standard Library - A Tutorial and Reference, 2nd Edition"
 * by Nicolai M. Josuttis, Addison-Wesley, 2012
 *
 * (C) Copyright Nicolai M. Josuttis 2012.
 * Permission to copy, use, modify, sell and distribute this software
 * is granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied
 * warranty, and with no claim as to its suitability for any purpose.
 */
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

class Person {
  public:
    string name;
    shared_ptr<Person> mother;
    shared_ptr<Person> father;
    vector<weak_ptr<Person>> kids;  // weak pointer !!!

    Person (const string& n)
     : name(n) {
    }

    void setParentsAndTheirKids (shared_ptr<Person> m = nullptr,
                                 shared_ptr<Person> f = nullptr) {
        mother = m;
        father = f;
        if (m != nullptr) {
            m->kids.push_back(shared_ptr<Person>(this));
        }
        if (f != nullptr) {
            f->kids.push_back(shared_ptr<Person>(this));
        }
    }

    ~Person() {
      cout << "delete " << name << endl;
    }
};

shared_ptr<Person> initFamily (const string& name)
{
    shared_ptr<Person> mom(new Person(name+"'s mom")); 
    shared_ptr<Person> dad(new Person(name+"'s dad")); 
    shared_ptr<Person> kid(new Person(name)); 
    kid->setParentsAndTheirKids(mom,dad); 
    return kid;
}

int main()
{
    shared_ptr<Person> p = initFamily("nico");
    cout << "nico's family exists" << endl;
    cout << "- nico is shared " << p.use_count() << " times" << endl;
    cout << "- name of 1st kid of nico's mom: " 
         << p->mother->kids[0].lock()->name << endl;

    p = initFamily("jim");
    cout << "jim's family exists" << endl;
}


这个例子的问题在于使用this作为shared_ptr,这是不可以的。

而应该采用如下方式:

/* The following code example is taken from the book
 * "The C++ Standard Library - A Tutorial and Reference, 2nd Edition"
 * by Nicolai M. Josuttis, Addison-Wesley, 2012
 *
 * (C) Copyright Nicolai M. Josuttis 2012.
 * Permission to copy, use, modify, sell and distribute this software
 * is granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied
 * warranty, and with no claim as to its suitability for any purpose.
 */
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

class Person : public enable_shared_from_this<Person> {
  public:
    string name;
    shared_ptr<Person> mother;
    shared_ptr<Person> father;
    vector<weak_ptr<Person>> kids;  // weak pointer !!!

    Person (const string& n)
     : name(n) {
    }

    void setParentsAndTheirKids (shared_ptr<Person> m = nullptr,
                                 shared_ptr<Person> f = nullptr) {
        mother = m;
        father = f;
        if (m != nullptr) {
            m->kids.push_back(shared_from_this());
        }
        if (f != nullptr) {
            f->kids.push_back(shared_from_this());
        }
    }

    ~Person() {
      cout << "delete " << name << endl;
    }
};

shared_ptr<Person> initFamily (const string& name)
{
    shared_ptr<Person> mom(new Person(name+"'s mom")); 
    shared_ptr<Person> dad(new Person(name+"'s dad")); 
    shared_ptr<Person> kid(new Person(name)); 
    kid->setParentsAndTheirKids(mom,dad); 
    return kid;
}

int main()
{
    shared_ptr<Person> p = initFamily("nico");
    cout << "nico's family exists" << endl;
    cout << "- nico is shared " << p.use_count() << " times" << endl;
    cout << "- name of 1st kid of nico's mom: " 
         << p->mother->kids[0].lock()->name << endl;

    p = initFamily("jim");
    cout << "jim's family exists" << endl;
}


注意shared_from_this()是不能在构造器里面使用的,会导致一个runtime error,因为shared_ptr把自己存放在Person基类的私有成员里面了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: