2016-06-09 2 views
1

Я пишу обертку C++ вокруг объекта низкого уровня, который хранится в массиве символов. Я хотел бы иметь возможность управлять временем жизни моего класса с помощью интеллектуальных указателей. Несколько членов моего класса должны возвращать указатели в объект; поскольку они не являются отдельными выделениями, я не могу использовать стандартные интеллектуальные указатели для них, и эти указатели станут недействительными после отпускания основного объекта.Умные указатели в пределах управляемого массива умных указателей

Для конкретности рассмотрим следующий простой класс строк:

class 
String 
{ 
    char* s; 
    size_t len; 

protected: 
    String(const char* str) { 
     len = std::strlen(str); 
     s = new char[len]; 
     std::strcpy(s, str); 
    } 
public: 
    ~String() { 
     delete s; 
    } 

    char* 
    getString() { 
     return s; 
    } 

    char* 
    getSubstr(size_t idx) { 
     if (idx < len) 
      return &s[idx]; 
     else 
      return nullptr; 
    } 

    static std::shared_ptr<String> 
    makeString(const char* str) { 
     return std::shared_ptr<String>(new String(str)); 
    } 
}; 

Я могу управлять экземплярами String с помощью std::shared_ptr. Но когда мой объект String будет уничтожен, любые указатели, возвращаемые getString() или getSubstring(), будут недействительными. Я бы как иметь способ для getString() и getSubstring() вернуть какую-то смарт-указатель, который будет содержать ссылку на родительский объект, так что следующий код действителен:

std::shared_ptr<String> str = String::makeString("Hello world"); 
SOMETHING<char> substr = str->getSubstr(6); 

std::printf("String: %s\n", str->getString()); 
str.reset(); 
std::printf("Substring: %s\n", substr.get()); 

Есть ли способ, чтобы достичь используя стандартную функциональность C++? Если нет, как насчет Boost?

Update

Вот версия, которая, кажется, работает (при компиляции в C++ 11 с помощью GCC или лязг), на основе предложений Yakk в:

#include <cstring> 
#include <boost/enable_shared_from_this.hpp> 
#include <boost/shared_ptr.hpp> 

using boost::shared_ptr; 
using boost::enable_shared_from_this; 

class 
String : public enable_shared_from_this<String> 
{ 
    size_t len; 
    char* s; 

protected: 
    String(const char* str) : len(std::strlen(str)), s(new char[len]) { 
     std::copy(str, str+len, s); 
    } 
public: 
    ~String() { 
     delete s; 
    } 

    shared_ptr<char[]> 
    getString() { 
     return shared_ptr<char[]>(shared_from_this(), s); 
    } 

    shared_ptr<char[]> 
    getSubstr(size_t idx) { 
     if (idx < len) 
      return shared_ptr<char[]>(shared_from_this(), s+idx); 
     else 
      return shared_ptr<char[]>(); 
    } 

    static shared_ptr<String> 
    makeString(const char* str) { 
     return shared_ptr<String>(new String(str)); 
    } 
}; 

ответ

4

Вы, вероятно, хотите shared_ptr " aliasing ".

Если у вас есть поддержка C++ 1z, вы даже получите [].

Ваш «ЧТО-ТО» всего лишь shared_ptr.

std::shared_ptr<char> 
getSubstr(size_t idx) { 
    if (idx < len) 
     return {shared_from_this(), &s[idx]}; 
    else 
     return {}; 
} 

и добавить enable_shared_from_this к String.

В качестве альтернативы, управлять буфером непосредственно через shared_ptr в String:

std::shared_ptr<char> s; 

и

String(const char* str) { 
    len = std::strlen(str); 
    s = std::shared_ptr<char>(new char[len], std::default_deleter<char[]>{}); 
    std::strcpy(s.get(), str); 
} 

std::shared_ptr<char> 
getSubstr(size_t idx) { 
    if (idx < len) 
     return {s, s.get()+idx}; 
    else 
     return {}; 
} 

Преимущество этой стратегии заключается в том, что String не должна управляться совместно PTR - вам может вставить его в вектор или объявить его в стеке. C++ преуспевает в обычных типах.

Внутренний буфер тем временем управляется умным указателем, поэтому подстроки не могут болтаться.

Ядро этого конструктора псевдонимов. Общие указатели имеют 2 компонента: блок подсчета ссылок и указатель данных.

Блок подсчета ссылок имеет сильное количество, слабое количество и функцию уничтожения.

Конструктор сглаживания позволяет использовать блок подсчета ссылок общего указателя и произвольный указатель данных.Это предназначено для возврата общих указателей членам класса, которым управляет общий указатель, что в основном является тем, что вы делаете.

Смежные вопросы