class/struct 用法
本文主讲 class(类) 的用法,因为和 struct 就一个区别……
定义
class 类型名称 {
// 成员
};
可以添加的成员包括:
- 成员变量
- 成员函数(包括 构造函数 和 析构函数)
- 一堆……
class 成员默认 private 访问,也就是外界无法访问,那么想让外界访问的话,就有两种方法:
- 若是对外界完全公开,使用
public - 若是对外界部分公开,使用
protected或者friend
访问权限
- 被声明为
public的成员对外界完全公开。 - 被声明为
protected的成员对派生类完全公开。 - 被声明为
private的成员对外界完全不公开。
声明方式:
class 类型名称 {
权限类型:
};
没错, public/protected/private后面需要加一个 :,然后在下一行开始声明成员。
构造函数 & 析构函数
构造函数
构造函数 指的是,该类的变量在创建时所自动运行的函数。
这是一些注意事项:
- 构造函数没有返回值类型,注意,是没有,连
void也不要加。 - 构造函数和类的名称必须相同,如果类名称为
node,那么构造函数的名称也为node。
构造函数通常声明为 public 类型,否则外界无法直接构造。
使用方式:
class node {
private:
int to, val;
public:
node(int x, int y) {
to = x;
val = y; // 完成了对 node 的成员变量的初始化
cout << "node Initialized." << `\n`; // 没用的一句话
};
};
如果是初始化成员变量的话,那么还有另一种方式:
class node {
private:
int to, val;
public:
node(int x, int y) : to(x), val(y) {
cout << "node Initialized." << `\n`;
};
};
叫做 初始化列表,更推荐使用这个。
构造函数和普通函数一样,可以定义多个,只要参数列表不同,编译器会自动匹配合适的那个。
构造函数会自动做隐式转换,比如:
class node {
public:
ll val;
node(ll x) : val(x) { cout << val << `\n`; }
};
void def(node x) {
cout << x.val << `\n`;
}
int main() { def(10); }
这里 def 函数需要的是 node 类型的参数,我们输入一个 int 类型,编译器隐式转换(通过构造函数)为了 node 类型,所以可以正常编译。
在构造函数前加上 explicit 即可阻止隐式转换,必须使用显式转换了。
析构函数
析构函数 和 构造函数 反着来,该类的变量在销毁时所自动运行的函数。
注意事项和构造函数相同。
声明方式:
class node {
private:
int to, val;
public:
node(int x, int y) : to(x), val(y) { cout << "node Initialized." << `\n`; }; // 构造函数
~node() { cout << "finished." << `\n`; } // 析构函数
};
当然 OI 中一般也用不到,没什么好讲的。
重载运算符
这要讲吗???
friend(友元)
对于声明为 private 的成员,我们该怎么访问/调用它们呢,要么改为 public,要么用另一个 public 的成员函数来间接调用,要么就用友元。
我只能说,友元最好用。
friend 的声明不受权限限制,放在哪里效果一样(当然要放在类的内部),它的作用就是给予另一个 函数/类 访问自身所有成员的权限。
举个例子:
class node {
friend void qwq();
private:
int to, val;
public:
node(int x, int y) : to(x), val(y) { cout << "node Initialized." << `\n`; };
~node() { cout << "finished." << `\n`; }
} awa;
inline void qwq() { cout << awa.to << ` ` << awa.val << `\n`; }
显然这里的 to 和 val 外部不可访问,但是 node 通过 friend 给予了 qwq 函数这个权限。
这是给予函数权限的例子,给予另一个类权限的例子我不写了,照搬即可。
谨慎使用友元啊!!!
派生类
基本用法
其实一般 OI 中也用不到。。。
派生类和基类的关系,打个比方:基类是 载具,派生类就是 步战车、固定翼 等。
还是举个例子:
class node {
private:
int to, val;
public:
node(int x, int y) : to(x), val(y) {};
};
class QwQ : node {
// 成员
};
这里 node 是基类,QwQ 是派生类。
派生类继承基类的所有成员(但是声明 private 的那些成员无法访问)。
继承这个动作也是有三种权限的(还是那三个),会导致派生类继承的成员的权限变化,总结一下就是:
- 基类的
private成员被继承后,派生类也无法访问 public继承权限不变protected继承会把原先public的成员继承为protectedprivate继承会把继承的所有成员全部变为private
class 的默认继承权限是 private。
class node {
private:
int to, val;
public:
node(int x, int y) : to(x), val(y) {};
};
class QwQ : 权限类型 node {
// 成员
};
using 提升权限
那么假设我 private 继承,但是想保留基类的某个成员的 public 权限,怎么办?
class node {
public:
int to, val;
node(int x, int y) : to(x), val(y) {};
};
class QwQ : private node {
// 这里的 to 和 val 现在是 private,外界不可访问
using node::to;
using node::val;
// 现在可以了
};
但是对于基类的 private 部分,由于其在派生类内是不可见的,所以不能使用 using 来获取访问权限。
using 还可以用来继承基类的构造函数,就不需要再一行一行重新写构造函数来调用它们的。
菱形继承 和 虚继承
啊。。。还有一个 菱形继承 和 虚继承。
假设 B 和 C 都是 A 的派生类,现在 D 同时继承 B 和 C,你猜猜会发生什么(没错,一个类可以继承多个基类)。
那么 D 就相当于继承了两份 A,这样会在调用时有歧义。
对于有歧义:
// 假设 A 有成员变量 val,现在有一个 D 的变量 d
d.val = 1;
// 这里就不知道被修改的 val 到底是哪个 val 了,所以需要:
d.B::val = 1;
// 声明是从 B 继承的 A 中的 val 被修改了
// 或者在声明的时候这样:
class A {};
class B : virtual A {};
class C : virtual A {};
class D : B, C {};
// virtual 和 A 之间可以加继承权限声明
// 就不会有两份 A 了,可以直接使用 d.val = 1;
// 但是也有额外的内存消耗,尽量少用吧
差不多了吧,其它的没什么必要讲了。
关于 class 和 struct 的区别,还记得我提到的 class 默认 private 吗?struct 默认 public,然后没区别了。
不过一个普遍的习惯是,对于一些纯粹的变量整合,用 struct,对于带有较多函数的,用 class。
比如我用 struct 封装带权图的边,因为只是存储功能,用 class 封装整棵线段树,因为有很多函数。
番外:前置定义
假设现在有两个函数 A、B,但是 A 会调用 B,B 会调用 A,怎么办?
那么就要前置定义其中一个了,我选择前置定义 A。
void A(); // 前置定义 A,不写函数主体
void B() { A(); } // 正常调用
void A() { B(); } // 补充完整的 A 函数,不过必须保证参数和前置定义的一样,否则会被视为另一个函数
class 也可以这样玩。
这玩意在多文件项目中挺常见的。

说些什么吧!