我有相当长的一段时间(数月)没有更新文章了。
并不是我放弃了写技术文章,而是因为这段时间,我把精力主要放在了图像智能算法的学习上了。去年12月时,我对图像智能算法还懵懵懂懂,做着 Linux 嵌入式应用程序开发,现在我已经正式入职到算法公司了,收入翻了一番,哈哈,不过累了好多。废话就到这里,我的转行辛酸史,以后再详谈。
坦诚的说,就编程语言而言,因为一直在做 Linux 嵌入式程序开发的缘故,我对C语言更加了解一点,读者应该能够发现,我之前的大部分文章都是关于C语言的。不过现在做算法,C++更加适合。C++和C语言虽然很像,但是在细节上还是有所区别的。今天阅读前辈同事的代码时,注意到了 struct 关键字,这个关键字在C++中做了不少扩展。本文将尝试做下总结。
C语言与C++中的struct
struct关键字是C语言中非常重要的关键字,在实际的C语言程序开发中,struct 不仅可以用于封装各种复杂的数据结构,还能够实现一些开发技巧——比如辅助数组赋值、模拟类等等,这些我之前的文章都讨论过。C++不仅保留了C语言中 struct 的功能,还做了不少扩展,具体的可以通过下面这个表说明:
请看下面这段C++代码示例,我们首先使用 struct 定义了 S1,接着又定义了 S2,S2 继承了 S1,因此我们可以在 test2 中使用 S1 中定义的成员 a、b。同时也可以看出,C++中的 struct 还可以定义成员函数,包括构造函数和析构函数。
#include <iostream>
using namespace std;
struct S1 {
int a;
int b;
S1() {
a = 1;
b = 2;
}
void print_mem() {
cout << "a = "<< a << ", b = "<<b <<endl;
}
};
struct S2: S1 {
int c;
~S2() {
cout << "S2 exit, c = " << c << endl;
cout << "a = "<< a << ", b = "<<b <<endl;
}
};
int main()
{
S1 test1;
test1.print_mem();
S2 test2;
test2.a = 3;
test2.b = 4;
test2.c = 5;
return 0;
}
编译并执行上述C++代码,不出意外地得到如下输出:
$ g++ t1.cpp
$ ./a.out
a = 1, b = 2
S2 exit, c = 5
a = 3, b = 4
C++ 中的 struct 和 class 关键字
从上面的例子不难看出,C++中的 struct 除了像C语言那样可以定义数据结构外,还可以像 class 关键字那样定义成员函数。不过,二者是有区别的。
成员的默认访问控制属性
首先,struct 默认的访问控制属性是 public,而 class 默认的访问控制属性是 private,这一点可以通过下面这段C++代码示例看出:
struct A {
int a;
};
class B {
int b;
};
A ta;
ta.a = 1; // 没有问题
B tb;
tb.b = 2; // 编译报错
继承的默认访问控制属性
类似的,在继承的过程中,struct 和 class 关键字的默认访问控制属性也是有所区别的:struct 默认 public,class 默认 private,例如下面这段C++ 代码:
struct A {
int a;
};
struct B: A {
int b;
};
B tb;
tb.a = 1; // 正常
这是没有问题的,对象 tb 可以访问由 A 继承而来的成员 a。但是如果将 B 的 struct 改为 class,也即:
struct A {
int a;
};
class B: A {
int b;
};
B tb;
tb.a = 1; // 编译报错
此时编译就会报错,提示“‘int A::a’ is inaccessible”,因为 class 的默认继承属性为 private。现在我们将 A 的 struct 修改为 class,B 的 class 再改回 struct,会发现,对象 tb 依然能够正常访问成员 a:
class A {
public:
int a;
};
struct B: A {
int b;
};
B tb;
tb.a = 1; // 正常
应注意,这里的讨论重点是继承的默认访问控制属性,因此我们将 A 中的 a 定义为 public 的。
可以看出,当 class 和 struct 混合使用时,默认的访问控制属性由子类决定,而不是由基类决定。不过,依赖默认属性不是特别清晰的写法,在实际的C++程序开发中,更推荐的做法是指明继承的方式:
class B: private A {
...
};
class B: public A {
...
};
定义模板
相较于C语言,C++还能够定义模板函数,请看下面这段代码示例:
template<typename T>
void fun(T num) {
cout << "num = " << num << endl;
}
fun(1); // num = 1
fun(1.01); // num = 1.01
在一些开源工程中,我还发现过有使用 class 定义模板函数的,也即使用class替换typename关键字:
template<class T>
void fun(T num) {
cout << "num = " << num << endl;
}
这种定义方式和上面使用typename的定义方式完全相同,但是 struct 就不能用于定义模板函数,如果读者尝试了,应该会得到编译报错的结果。
小结
本文先是讨论了 struct 关键字在C语言和C++中的不同,不难发现,C++对 struct 关键字是做了不少扩充的,这些扩充让 struct 看起来更像是 class 关键字。事实上,我认为就单纯C++来说,class 关键字是完全可以取代 struct 的,C++ 仍然保留着 struct 关键字,其中一个重要原因就是兼容C语言。既然保留了 struct 关键字,总不能让它完全等价于 class,因此二者在一些细节上有区别,这些轻微的区别往往能够针对不同的需求提供不同的方便:struct 更适合封装数据结构,class 则更适合封装对象。