指向成员的指针操作符
指向成员的指针操作符可以让你在一个类的任何实例上描述指向某个成员的指针。有两种pointer-to-member操作符,取值操作符*和指针操作符->:
#include
usingnamespacestd;
structTest {
intnum;
voidfunc() {}
};
// Notice the extra "Test::" in the pointer type
intTest::*ptr_num = &Test::num;
void(Test::*ptr_func)() = &Test::func;
intmain() {
Test t;
Test *pt = newTest;
// Call the stored member function
(t.*ptr_func)();
(pt->*ptr_func)();
// Set the variable in the stored member slot
t.*ptr_num = 1;
pt->*ptr_num = 2;
deletept;
return0;
}
该特征实际上十分有用,尤其在写库的时候。例如,Boost::Python, 一个用来将C++绑定到Python对象的库,就使用成员指针操作符,在包装对象时很容易的指向成员。
#include
#include
usingnamespaceboost::python;
structWorld {
std::string msg;
voidgreet() { std::cout <
};
BOOST_PYTHON_MODULE(hello) {
class_("World")
.def_readwrite("msg", &World::msg)
.def("greet", &World::greet);
}
记住使用成员函数指针与普通函数指针是不同的。在成员函数指针和普通函数指针之间casting是无效的。例如,Microsoft编译器里的成员 函数使用了一个称为thiscall的优化调用约定,thiscall将this参数放到ecx寄存器里,而普通函数的调用约定却是在栈上解析所有的参 数。
而且,成员函数指针可能比普通指针大四倍左右,编译器需要存储函数体的地址,到正确父地址(多个继承)的偏移,虚函数表(虚继承)中另一个偏移的索引,甚至在对象自身内部的虚函数表的偏移也需要存储(为了前向声明类型)。
#include
structA {};
structB :virtualA {};
structC {};
structD : A, C {};
structE;
intmain() {
std::cout <
std::cout <
std::cout <
std::cout <
return0;
}
// 32-bit Visual C++ 2008: A = 4, B = 8, D = 12, E = 16
// 32-bit GCC 4.2.1: A = 8, B = 8, D = 8, E = 8
// 32-bit Digital Mars C++: A = 4, B = 4, D = 4, E = 4
在Digital Mars编译器里所有的成员函数都是相同的大小,这是源于这样一个聪明的设计:生成“thunk”函数来运用右偏移而不是存储指针自身内部的偏移。
静态实例方法
C++中可以通过实例调用静态方法也可以通过类直接调用。这可以使你不需要更新任何调用点就可以将实例方法修改为静态方法。
structFoo {
staticvoidfoo() {}
};
// These are equivalent
Foo::foo();
Foo().foo();
重载++和–
C++的设计中自定义操作符的函数名称就是操作符本身,这在大部分情况下都工作的很好。例如,一元操作符的-和二元操作符的-(取反和相减)可以通 过参数个数来区分。但这对于一元递增和递减操作符却不奏效,因为它们的特征似乎完全相同。C++语言有一个很笨拙的技巧来解决这个问题:后缀++和–操作 符必须有一个空的int参数作为标记让编译器知道要进行后缀操作(是的,只有int类型有效)。
structNumber {
Number &operator ++ (); // Generate a prefix ++ operator
Number operator ++ (int);// Generate a postfix ++ operator
};
操作符重载和检查顺序
重载,(逗号),||或者&&操作符会引起混乱,因为它打破了正常的检查规则。通常情况下,逗号操作符在整个左边检查完毕才开始检 查右边,|| 和 &&操作符有短路行为:仅在必要时才会去检查右边。无论如何,操作符的重载版本仅仅是函数调用且函数调用以未指定的顺序检查它们的参数。
重载这些操作符只是一种滥用C++语法的方式。作为一个实例,下面我给出一个Python形式的无括号版打印语句的C++实现:
#include
namespace__hidden__ {
structprint {
boolspace;
print() : space(false) {}
~print() { std::cout <
template
print &operator , (constT &t) {
if(space) std::cout <
elsespace =true;
std::cout <
return*this;
}
};
}
#define print __hidden__::print(),
intmain() {
inta = 1, b = 2;
print "this is a test";
print "the sum of", a,"and", b,"is", a + b;
return0;
}