记一次C++的纠错过程
成员函数和非成员函数指针
事出的起因是我在leetcode刷着一道题,需要排序,于是我就自定义了一个比较函数,代码如下:
1 class Solution {
2 public:
3 inline int digit(int x) {
4 if (!x) return 10;
5 int ret = 1;
6 while (x) ret *= 10, x /= 10;
7 return ret;
8 }
9 inline int cmp(int a, int b) {
10 long long ab = 1LL * a * digit(b) + b, ba = 1LL * b * digit(a) + a;
11 return ab > ba;
12 }
13 string largestNumber(vector<int> nums) {
14 sort(nums.begin(), nums.end(), Solution::cmp);
15 string ret;
16 char t[100];
17 for (int i = 0; i < nums.size(); i++) {
18 if (ret == "0" && !nums[i]) continue;
19 sprintf(t, "%d", nums[i]);
20 ret = ret + t;
21 }
22 return ret;
23 }
24 };
结果这个函数死活通过不了编译,乱改乱改还出现了invalid use of non-static member function 问题。看来成员函数不让这么弄,翻阅C++大师Stanley Lippman的Essential C++找一下成员函数的相关用法,还参考了一些技术博客,原来是C++的语法不过关啊!!/哭
下面回顾一下我纠错的过程,先看下面一段代码熟悉一下怎么调用普通的函数指针:
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 int F(double x) { return int(x); }
6
7 int G(double x) { return int(x) + 1; }
8
9 int main() {
10 int (*p1)(double) = &F;
11 int (*p2)(double) = &G;
12 cout << (*p1)(1) << endl;
13 cout << (*p2)(1) << endl;
14 return 0;
15 }
如果用指针指向类的成员函数的时候就稍有不同,试着根据自己的想法改一下,主要是加上类的scope运算符限定一下函数的范围:
/*zhen hao*/
#include <bits/stdc++.h>
using namespace std;
class C {
public:
C() {}
int F(double x) { return int(x); }
int G(double x) { return int(x) + 1; }
};
int main() {
int (C::*p1)(double) = &C::F; //编译通过
int (C::*p2)(double) = &C::G; //编译通过
cout << (C::*p1)(1) << endl; //error: expected unqualified-id before '*' token
cout << (C::*p2)(1) << endl; //error: expected unqualified-id before '*' token
return 0;
}
想当然以为简单改一下可以,然而编译不通过问题出在哪里呢?编译器报的错误是什么意思?
(1)先简单解释一下这个qualified是什么意思?
单词的意思是有限制的,也就是一些有范围的变量,例如下面的代码:
1 #include <iostream>
2 int main() {
3 std::cout<<"Hello world!"<<std::endl; //cout和endl都是qualified name,因为他们都限定在std这个明明空间上。
4 return 0;
5 }
值得注意的是,如果我们之间在代码上指定命名空间的话,例如using namespace std,那么cout和endl就是unqualified name了。
(2)另外id的全称是identifier,即标识符。
所以上述编译器希望这是一个qualified-id,也就是希望p1,p2是一个不需要用C::限定的变量,去掉之后仍然报错,报的错误是invalid use of 'unary *' on pointer to member,不能够用指针指着成员直接使用。
书上指出,pointer to member function和pointer to function的不同点是,前者必须通过同一类对象加以调用,而该对象便是此member function内的this指针所指之物。
所以以下用法才是正确的:
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 class C {
6 public:
7 C() {}
8 int F(double x) { return int(x); }
9 int G(double x) { return int(x) + 1; }
10 };
11
12 int main() {
13 int (C::*p1)(double) = &C::F;
14 int (C::*p2)(double) = &C::G;
15 C *c = new C();
16 cout << (c->*p1)(1) << endl;
17 cout << (c->*p2)(1) << endl;
18 return 0;
19 }
原来我们需要的是一个实例来调用成员函数指针。
到这里就要问为什么呢?
因为代码中定义了C类并没有初始化这个类的成员函数。当你声明了具体的实例之后,一个实例调用构造函数被初始化了,所以函数指针才能指向具体的类的成员函数。那如果想要传一个成员函数指针给其他函数调用该怎么做?这就要利用static对象的特性了,因为static的生存周期是从被构造出来到程序结束,也就是程序编译之后就被初始化好了。
所以我们将上述代码改一下就能改正错误的代码了:
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 class C {
6 public:
7 C() {}
8 static int F(double x) { return int(x); }
9 static int G(double x) { return int(x) + 1; }
10 };
11
12 int main() {
13 int (*p1)(double) = &C::F;
14 int (*p2)(double) = &C::G;
15 cout << (*p1)(1) << endl;
16 cout << (*p2)(1) << endl;
17 return 0;
18 }
用static关键字修饰之后的成员函数就像普通函数一样可以传给别的函数做参数,使用也像普通函数一样。
总结
这次的经历让我认识到什么叫在错误中成长。一点一点积累,厚积薄发才能够写出健壮的代码。