0

我正在设计一个包装类来提供 RAII 功能。原始用例如下:

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

在我引入一个新的包装类之后,我预计未来的用法如下:

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

或者

TTTB(tid);

问题:

哪个实施TTTATTTB更好?或者他们都不好,请介绍一个更好的。

我担心的一件事是,在分配资源后,id将在课堂外访问TTTATTTB直到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
}
4

3 回答 3

5

两者都以不好的方式打破。

TTTA 存储对存储在堆栈中的变量(参数 id)的引用。
TTTB 存储指向存储在堆栈中的变量的指针。

两次,当构造函数返回时,变量都会超出范围。

编辑:由于您希望可以修改值,因此最简单的解决方法是将指针作为参考;这将使 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;所以你的结果可能会有所不同。

于 2013-05-21T19:15:22.547 回答
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在标准库中有一个自定义删除器,实际上实现了相同的效果,并且还正确地实现了三规则。

于 2013-05-21T19:12:49.037 回答
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());
于 2013-05-21T19:13:22.363 回答