2013-05-21 16 views
0

Я работаю над созданием класса-оболочки для обеспечения функции RAII. оригинальный вариант использования следующим образом:RAII - хранить `void * &` или `void **`

void* tid(NULL); 
OpenFunc(&tid); 
CloseFunc(&tid); 

После того как я Вводится новый класс-обертку, я ожидаю, что в будущем использование будет выглядеть следующим образом:

void* tid(NULL); 
TTTA(tid); 

или

TTTB(tid); 

Вопрос :

Какая реализация TTTA или TTTB лучше? Или они все плохие, и, пожалуйста, представите лучшую.

Одна вещь, которую я озабоченность в том, что после того, как ресурс выделен, то id будет доступен за пределами класса TTTA или TTTB до id разрушается. Основываясь на моем понимании, мой дизайн не должен иметь побочного эффекта для этого.

Спасибо

class TTTA : boost::noncopyable 
{ 
public: 
    explicit TTTA(void *id) 
     : m_id(id) 
    { 
     OpenFunc(&m_id); // third-party allocate resource API 
    } 

    ~TTTA() 
    { 
     CloseFunc(&m_id); // third-party release resource API 
    } 
private: 
    void* &m_id; // have to store the value in order to release in destructor 
} 

class TTTB : boost::noncopyable 
{ 
public: 
    explicit TTTB(void *id) 
     : m_id(&id) 
    { 
     OpenFunc(m_id); // third-party allocate resource API 
    } 

    ~TTTB() 
    { 
     CloseFunc(m_id); // third-party release resource API 
    } 
private: 
    void** m_id; // have to store the value in order to release in destructor 
} 

// Передаем в сравнении указателей

class TTTD 
{ 
public: 
    TTTD(int* id) // Take as reference, do not copy to stack. 
     : m_id(&id) 
    { 
     *m_id = new int(40); 
    } 

private: 
    int** m_id; 
}; 


class TTTC 
{ 
public: 
    TTTC(int* &id)  
     : m_id(id) 
    { 
     m_id = new int(30); 
    } 

private: 
    int* &m_id; 
}; 

class TTTB 
{ 
public: 
    TTTB(int* id)  
     : m_id(id) 
    { 
     m_id = new int(20); 
    } 

private: 
    int* &m_id; 
}; 

class TTTA 
{ 
public: 
    TTTA(int** id)  
     : m_id(id) 
    { 
     *m_id = new int(10); 
    } 

private: 
    int** m_id; 
}; 


int main() 
{ 
    ////////////////////////////////////////////////////////////////////////// 
    int *pA(NULL); 
    TTTA a(&pA); 
    cout << *pA << endl; // 10 

    ////////////////////////////////////////////////////////////////////////// 
    int *pB(NULL); 
    TTTB b(pB); 
    //cout << *pB << endl; // wrong 

    ////////////////////////////////////////////////////////////////////////// 
    int *pC(NULL); 
    TTTC c(pC); 
    cout << *pC << endl; // 30 

    ////////////////////////////////////////////////////////////////////////// 
    int *pD(NULL); 
    TTTD d(pD); 
    cout << *pD << endl; // wrong 
} 
+0

В C++ вы обычно используете ссылки для изменения объектов внутри функции. –

+1

Основываясь на моем понимании, оба «TTTA» и «TTTB» эквивалентны. – q0987

+0

Нет, один использует ссылку, другой использует указатель. –

ответ

5

И разрыв в плохих отношениях.

В TTTA хранится ссылка на переменную (идентификатор параметра), которая хранится в стеке.
TTTB хранит указатель на переменную, которая хранится в стеке.

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

EDIT: Поскольку вы хотите, чтобы значения изменялись, самым простым решением является указание указателя в качестве ссылки; что будет делать TTTC ссылаться на фактический указателе вместо локальной копии сделали, принимая указатель в качестве параметра, не эталонного;

class TTTC : boost::noncopyable 
{ 
public: 
    explicit TTTA(void *&id) // Take as reference, do not copy to stack. 
     : m_id(id) 
... 
private: 
    void* &m_id; // have to store the value in order to release in destructor 
} 

Простой тест, который нарушает ваши версии, чтобы добавить метод print к классам, чтобы напечатать значение указателя и делать;

int main() { 

    void* a = (void*)0x200; 
    void* b = (void*)0x300; 

    { 
    TTTA ta(a); 
    TTTA tb(b); 

    ta.print(); 
    tb.print(); 
    } 
} 

Оба TTTA и TTTB печатают оба значения как 0x300 на моей машине. Конечно, результат действительно UB; поэтому ваш результат может отличаться.

+0

Ваше простенькое исправление не работает для меня. Если вы берете 'void * m_id' в качестве параметра прохода, вы не можете изменить параметр пропуска. Также я ожидаю, что внешний tid будет недействительным, когда объект TTTA вернется. Для вашего примера я не понимаю, в чем проблема, так как печать вызывается внутри области действия, и результат должен быть действительным. – q0987

+1

@ q0987 См. Мое редактирование для модифицируемой версии. Проблема с TTTA/TTTB заключается в том, что оба воспринимают указатель как не ссылочный параметр, это означает, что он скопирован в стек в конструкторе, указатель/ссылка берется в местоположение в стеке и когда конструктор возвращается, указатель/ссылка остается болтающейся. Если вы берете параметр в качестве ссылки, то переданный в указателе вместо этого указывает на. –

+0

Я обновил свой OP и основываясь на ваших комментариях, теперь я знаю, что оба «TTTA» и «TTTB» ошибаются! – q0987

2

Почему вы tid вообще? Это утечка информации клиенту и делает использование в два раза дольше (две строки вместо одной):

class tttc { 
    void* id; 

public: 

    tttc() { 
     OpenFunc(&id); 
    } 

    ~tttc() { 
     CloseFunc(&id); 
    } 

    tttc(tttc const&) = delete; 
    tttc& operator =(tttc const&) = delete; 
}; 

Обратите внимание, что этот класс запрещает копирование - ваши решения нарушить правило трех.

Если вам необходим доступ к id извне, обеспечивают преобразование внутри tttc:

void* get() const { return id; } 

Или, если это абсолютно необходимо, с помощью неявного преобразования:

operator void*() const { return id; } 

(Но использовать этот один разумно, так как неявные преобразования ослабляют систему типов и могут привести к затруднению диагностики ошибок.)

И вот std::unique_ptr в стандартной библиотеке, которая с обычным делетом фактически достигает того же и дополнительно реализует правило из трех должным образом.

+0

b/c 'tid' используется в других функциях, и я не обертываю эту функцию внутри класса-оболочки. – q0987

+0

@ q0987 Затем укажите преобразование из 'tttc'. См. Обновление. –

+0

Вы имеете в виду обернуть 'tid' внутри класса TTT и предоставить функцию accessor/getter, которая возвращает идентификатор? Я не уверен, что это хорошая идея. Так как другой API также может изменять tid. – q0987

2

Как обернуть его полностью? Таким образом, вам не нужно беспокоиться об управлении жизненными циклами двух переменных, но только одним.

class TTTC 
{ 
    void* m_id; 
public: 
    TTTC() 
     : m_id(nullptr) 
    { 
     OpenFunc(&m_id); // third-party allocate resource API 
    } 

    TTTC(TTTC const&) = delete; // or ensure copying does what you expect 

    void*const& tid() const { return m_id; } 

    ~TTTC() 
    { 
     CloseFunc(&m_id); // third-party release resource API 
    } 
}; 

Используя это сама простота:

TTTC wrapped; 
DoSomethingWithTid(wrapped.tid()); 
Смежные вопросы