考虑以下程序:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
Run Code Online (Sandbox Code Playgroud)
我怎么得到clyde地址?
我正在寻找一种适用于所有类型对象的解决方案.C++ 03解决方案会很好,但我也对C++ 11解决方案感兴趣.如果可能,让我们避免任何特定于实现的行为.
我知道C++ 11的std::addressof函数模板,但我不想在这里使用它:我想了解标准库实现者如何实现这个函数模板.
让我们考虑一个带有重载的一元运算符和(Address-of)的类.随它去class A
template <class C>
class A
{
public:
C * operator &()
{
return &data;
}
//...
private:
C data;
}
Run Code Online (Sandbox Code Playgroud)
现在我想传递一些函数指针A来填充它data.我们称之为f
void f(A * auto_containter)
{
//...
}
Run Code Online (Sandbox Code Playgroud)
但很明显为什么代码波纹管不起作用(甚至不会编译).这是因为调用了重载运算符.
A a;
f(&a);
Run Code Online (Sandbox Code Playgroud)
问题如下:
是否有任何语法传递的地址a来f?如果不是,那么对我来说,为什么它允许超载一元是非常奇怪的operator &,因为它使代码更加错误和难以理解.还是有其他一些原因?
使用COM时,我通常依赖于ATL智能指针,如ATL::CComPtr和ATL::CComBSTR,用于资源管理.但是我正在调用的一些方法使用输出参数来返回指向我必须释放的已分配存储的指针.例如:
WCHAR *pszName = nullptr;
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) {
DoSomething(pszName);
CoTaskMemFree(pszName);
}
Run Code Online (Sandbox Code Playgroud)
请注意,GetDisplayName为字符串分配内存并通过输出参数返回指向它的指针.调用者有责任释放内存CoTaskMemFree.
如果DoSomething抛出异常,则上述代码将泄漏.我想使用某种智能指针pszName来避免这种泄漏,但API需要WCHAR**,所以我不知道除了哑指针的地址之外我怎么能传递.由于我不是那个分配,我不能使用RAII.
如果我可以像这样制作一个删除器,我可以使用RRID:
struct CoTaskMemDeleter {
void operator()(void *p) { ::CoTaskMemFree(p); }
};
Run Code Online (Sandbox Code Playgroud)
然后立即将返回的指针分配给标准智能指针,如下所示:
WCHAR *pszName = nullptr;
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) {
std::unique_ptr<WCHAR, CoTaskMemDeleter> guard(pszName);
DoSomething(pszName);
}
Run Code Online (Sandbox Code Playgroud)
这样可行,但似乎容易引入额外的保护变量.例如,这种方法pszName指向释放的内存,因此很容易意外地再次使用它.
对于输出参数返回的COM服务器分配的内存,是否有更简洁的方法来使用智能指针或RAII包装器?我错过了ATL提供的东西吗?
使用&如果变量类型已超载获取变量的地址可能会有问题operator&().例如,_com_ptr_已经operator&()重载,具有修改对象的副作用.
现在我有一组复杂的模板,其功能如下:
template<class T>
void process( const T* object )
{
//whatever
}
template<class T>
void tryProcess( T& object )
{
process( &object )
}
Run Code Online (Sandbox Code Playgroud)
在tryProcess()我需要得到一个T*字保存类型的实际对象的地址T.
tryProcess()如果class T没有operator&()重载,上面的实现只会正常工作.所以,如果我打电话,tryProcess<_com_ptr_<Interface>>()我可以得到意想不到的结果 - 重载operator&()被触发.
template<class T>
T* getAddress( T& object )
{
return reinterpret_cast<T*>( &reinterpret_cast<char&>( object ) );
}
Run Code Online (Sandbox Code Playgroud)
有了这样的功能,我可以实现tryProcess()如下:
template<class T>
void tryProcess( …Run Code Online (Sandbox Code Playgroud) 有时使用a的起始地址std::vector并暂时将该地址视为定期分配的缓冲区的地址是有用的.
例如替换这个:
char* buf = new char[size];
fillTheBuffer(buf, size);
useTheBuffer(buf, size);
delete[] buf;
Run Code Online (Sandbox Code Playgroud)
有了这个:
vector<char> buf(size);
fillTheBuffer(&buf[0], size);
useTheBuffer(&buf[0], size);
Run Code Online (Sandbox Code Playgroud)
这样做的好处当然是缓冲区自动解除分配,我不必担心delete[].
我遇到的问题是当size == 0.在这种情况下,第一个版本正常工作.一个空缓冲区被"分配",后续函数没有任何大小,它们得到size == 0.
然而,如果size == 0,则第二个版本失败,因为调用buf[0]可能正确地包含一个断言0 < size.
那么&buf[0]即使向量是空的,还有一个成语的替代方法返回向量的起始地址吗?
我也考虑过使用,buf.begin()但根据标准,它甚至不能保证返回指针.