1. 静态成员函数
1.1 静态成员函数调用
如何代码块里的函数get_object_count
是静态成员函数,对静态成员函数无论是通过类名还是通过对象或对象指针调用,最终都会被编译器转换为全局非成员函数的调用。例如obj.get_object_count()
或Point3d::get_object_count()
都会被编译器通过名称处理后转化为_ZN7Point3d16get_object_countEv()
。
在c++
没有提供静态函数成员时,或想调用类里的一个函数,且此函数并没有使用类的任何数据成员,类似下面第9行的函数num_test
,则可以使用((Point3d *) nullptr)->num_test()
这样的方式,注意这种方式是将空指针作为this
指针传递进成员函数内,所以在这个函数内是不能够访问数据成员的,否则通过空指针调用会引发异常。
因此可以发现,若一个成员函数没有使用任何类非静态函数成员与非静态数据成员,那么这个成员函数是不需要this
指针的,因此也就没必要通过一个类对象来调用,所以当然应该把这个成员函数设置为静态的。
class Point3d {
public:
static int object_count;
static int get_object_count() {
return object_count;
}
int num_test() {
return object_count;
}
private:
double x{}, y{}, z{};
};
int Point3d::object_count = 10;
double Point3d::magnitude() const { return sqrt(x * x + y * y + z * z); }
double magnitude(const Point3d *ptr);
int main() {
std::cout << Point3d::get_object_count() << std::endl;
printf("%p\n", &Point3d::get_object_count);
std::cout<<((Point3d *) nullptr)->num_test();
return 0;
}
当然即使采用对象调用静态成员函数,经过编译器的处理最终也是调用一个全局函数,是不会给这个函数传递this
指针的。如下所示的第一行的通过类对象调用的方法会在编译器内部转换为3-4
行的调用方式,注意虽然这个匿名对象在第4
行是没有用的,但是仍然需要生成,因为这个生成有可能会改变程序执行,所以不能直接省略。
if (Point3d().get_object_count() > 1) {}
// (void) Point3d();
// if (Point3d::object_count > 1) {}
1.2 回调函数
回调函数的讲解可以参考,其作用在于对函数的解耦和复用。例如如下的代码就是一个回调函数的例子,调用sort
函数的主函数可以被称为起始函数,bubble_sort
函数是中间函数,less
函数是回调函数。这里只需要一个中间函数就可以实现不同规则的排序,即可以从小到大或从大到小,只需要改变回调函数即可。
using Compare = bool (*)(int, int);
void bubble_sort(std::vector<int> &vec, Compare compare) {
//冒泡排序
for (auto i = 0; i < vec.size(); ++i) {
for (auto j = 1; j < vec.size() - i; ++j) {
if (compare(vec[j - 1], vec[j])) {
std::swap(vec[j - 1], vec[j]);
}
}
}
}
bool less(int i, int j) {
return i > j;
}
int main() {
std::vector<int> vec{2, 4, 1, 6, 5};
bubble_sort(vec, less);
std::for_each(vec.begin(), vec.end(), [](int i) { std::cout << i << " "; });
return 0;
}
回调函数和成员函数的关系在于:回调函数不能写成成员函数。如下的代码是无法编译执行的,同时第10-11
行也是无法执行的,它们的逻辑是一致的:不能通过函数地址直接调用类非静态成员函数,因为这种函数隐含了一个this
指针,如果不通过类对象去调用,是无法传递this
指针的,会出现缺少参数的错误。
第二种错误理解角度是函数类型不一致,如下编译器报错,也就是说类成员函数testB
的实际类型是void(Point3d:: *)()
,而传递的函数指针类型为void (*)()
,类型不一致,所以无法编译通过,而这个解决办法即是将testB
改为static
类型,而static
的函数类型为void (*)()
。
using CallFunc=void (*)();
class backCall {
public:
void testA(CallFunc callFunc) {
cout << 1 << endl;
callFuc();
// auto f=&backCall::testB;
// f();
}
void testB() {
cout << 2 << endl;
}
};
int main() {
backCall().testA(&backCall::testA);
}