addressof

头文件: "boost/utility.hpp"

要取得一个变量的地址,我们要依赖于返回的值是否真的是这个变量的地址。但是,技术上重载operator&是有可能的,这意味着存有恶意的人可以破坏你的地址相关的代码。boost::addressof 被用于获得变量的地址,不管取址操作符是否被误用。通过使用一些灵巧的内部机制,模板函数 addressof 确保可以获得真实的对象及其地址。

用法

为确保获得一个对象的真实地址,你要使用 boost::addressof. 它定义在 "boost/utility.hpp". 它常用于原本要使用 operator& 的地方,它接受一个参数,该参数为要获得地址的那个对象的引用。

#include "boost/utility.hpp"

class some_class {};

int main() {
  some_class s;
  some_class* p=boost::addressof(s);
}

在进一步学习如何使用 addressof的细节前,了解一下operator&为何以及如何不一定会返回对象的地址是非常有用的。

快速了解一下存有恶意的人

如果你真的,真的,真的需要重载 operator&, 或者只是想试验一下操作符重载可能的用法,这的确很容易。当你重载 operator&时,它的语义肯定会与多数用户(以及函数!)所期望的不同,所以千万不要为了好玩而做这件事;除非有非常好的理由,否则不要去做它。以下有一段code-breaker代码:

class codebreaker {
public:
  int operator&() const {
    return 13;
  }
};

对于这个类,任何人想获取一个codebreaker实例的地址都会得到一个不可思议的数字13.

template <typename T> void print_address(const T& t) {
  std::cout << "Address: " << (&t) << '\n';
}

int main() {
  codebreaker c;
  print_address(c);
}

这不难做到,但是在实际的代码中这样做有没有好的理由?也许没有,因为除非是用在局部的类上,否则它是不安全的。原因是,虽然获取一个不完整类型的地址是合法的,但如果是要获取一个带有用户自定义operator&的不完整类型的地址则是未定义的行为。因为我们不能保证这不会发生,所以我们最好不要重载 operator&.

迅速的解决方法

即使一个类的 operator& 被重载了,也还是有办法获得这个类的实例的真实地址。addressof 使用了一些幕后的巧妙方法[6]来获得真实的地址,而不会受任何 operator& 的欺骗。如果你把函数(print_address)改为使用 addressof, 你就可以得到以下代码:

[6] 非常出名的一种ingenious hack.

template <typename T> void print_address(const T& t) {
  std::cout << "&t: " << (&t) << '\n';
  std::cout << "addressof(t): " << boost::addressof(t) << '\n';
}

执行时,该函数将给出如下输出(或类似于以下的输出,因为准确的地址值取决于你的系统).

&t: 13
addressof(t): 0012FECB13

差不多就是这样了!如果有什么情况让你知道或怀疑一个类的operator&被重载了,而你又需要确保得到真实的地址(由于 operator& 被重载而变得不可信了), 你就应该使用 addressof.

总结

没有多少有力的论点支持重载 operator&,[7] 但由于这是可能的,总有些人会这样做。当你编写一些需要依赖于获得对象真实地址的代码时,addressof 可以帮助你确保得到真实的地址。在编写泛型代码时,没有办法知道将会操作什么类型,因此如果需要获取参数化类型的地址的话,就使用 addressof.

[7] 即使是定制的硬件设备驱动程序

当你需要获得一个对象的真实地址时,使用 addressof ,不必管 operator& 的语义。