我正在设计一个包装类来提供RAII功能。 原始用例如下:
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 :(得分: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;所以你的结果可能会有所不同。
答案 1 :(得分: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
,它带有自定义删除器,实际上实现了相同的功能,并且还适当地实现了三个规则。
答案 2 :(得分: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());