1 可见性
可见性对性能没有任何影响,也不会产生不同的代码。只是人为规定,用于提示,可以类比类型的定义。
基类 | 友元 | 子类 | 外部 | |
---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
private | ✅ | ✅ | ❌ | ❌ |
可见性的意义:
允许或不允许自己或其他人访问类中的特定成员。将不想被直接访问的成员定义为private,保证代码的正常运行。
小例子:
一个摇杆控制方向的UI界面。该类定义如下:
class UI
{
private:
int x;
int y;
public:
void refresh(int direction)
{
// 用于刷新显示界面,控制摇杆移动方向
}
void move(int direction){
// 判断哪个方向移动多少,改变xy坐标值
// 并刷新显示,产生摇杆往对应方向移动的显示
refresh(direction);
}
};
如果直接修改xy的值,摇杆移动的画面时无法显示的。
2 class和struct的区别
本质区别:在不声明是public还是private的情况下,class默认是private,而struct默认是public。除此以外,本质上🈚️区别了。
一般情况下,struct用来维护数据,class用于更复杂的功能,如继承等。另外一个角度来看,用class来实例对象(产生个体)。
3 class小例子
不同的成员可以分开写,即使他们的可见性是相同的
例如将公共的数据和函数分别写在两个public中,这样比较好维护
这个小例子是一个关于日志输出的类:
class Log
{
public:
const int LogLevelError = 0;
const int LogLevelWarning = 1;
const int LogLevelInfo = 2;
private:
int m_LogLevel = LogLevelInfo;
public:
void SetLevel(int level)
{
m_LogLevel = level;
}
void Error(const char* message)
{
if (m_LogLevel>=LogLevelError)
cout << "[ERROR]:" << message << endl;
}
void Warn(const char* message)
{
if (m_LogLevel>=LogLevelWarning)
cout << "[WARNING]:" << message << endl;
}
void Info(const char* message)
{
if (m_LogLevel>=LogLevelInfo)
cout << "[INFO]:" << message << endl;
}
};
int main(){
Log log;
log.SetLevel(log.LogLevelWarning);
log.Warn("Hello");
log.Error("Hello");
log.Info("Hello");
// 输出有WARNING和ERROR
}
4 enum
枚举其实是一个人为添加的限制,尤其适用于不同状态要有对应操作的情况下,比如上述的Log。
要定义所有代表状态的变量并完成赋值,使用枚举可以将这些状态变量集中在一起定义,并且自动赋值(可以自己给任意变量赋值,如果不赋值,则默认第一个变量为0,其他变量值为前一个变量值+1),增加代码可读性。另外,也可以防止其他部分代码修改表示状态的变量值。
这里需要强调:
所有枚举变量值都是整数,但这不代表变量一定是int类型,这里回忆之前类型章节所述。由于枚举变量都是整数,因此可以将枚举变量定义成可以接受整数的类型,如char等,这意味着float、double是不行的。由于不同类型变量占用的空间不同,根据状态个数选择合适类型可以节省空间。
将上述Log用枚举实现:
class Log
{
public:
enum Level : char
{
// 命名需要注意,变量不要和函数重名,
// 所以这里都加上了Level作为前缀来区分变量
LevelError = 0, LevelWarning, LevelInfo
};
private:
int m_LogLevel = LevelInfo;
public:
void SetLevel(int level)
{
m_LogLevel = level;
}
void Error(const char* message)
{
if (m_LogLevel>=LevelError)
cout << "[ERROR]:" << message << endl;
}
void Warn(const char* message)
{
if (m_LogLevel>=LevelWarning)
cout << "[WARNING]:" << message << endl;
}
void Info(const char* message)
{
if (m_LogLevel>=LevelInfo)
cout << "[INFO]:" << message << endl;
}
};
int main(){
Log log;
log.SetLevel(Log::LevelWarning);
log.Warn("Hello");
log.Error("Hello");
log.Info("Hello");
// 输出有WARNING和ERROR
}
5 构造函数
负责类的初始化,与类同名,可重载
class Entity
{
public:
float X,Y;
// 构造1
Entity(){
X = 0;
Y = 0;
}
// 构造2
Entity(float x, float y)
{
X = x;
Y = y;
}
void Print(){
cout << X << ',' << Y << endl;
}
};
int main(){
// 不同构造方式
Entity a;
Entity b(1,1);
a.Print();
b.Print();
// 输出0,0和1,1
}
工具类:如果一个类只有一些静态函数,我们不希望使用者实例化这个类的对象,而是直接使用这个类中的方法,这就需要对构造函数进行操作:
方式1:构造函数私有化
方式2:删除构造函数(推荐)
// 方式1
class Log
{
private:
Log(){}
public:
static void Write(){
// 巴拉巴拉
}
};
// 方式2
class Log
{
public:
Log() = delete;
static void Write(){
// 巴拉巴拉
}
};
5.1 成员初始化列表
在使用构造函数初始化类成员变量时,成员初始化列表是更好的方式。需要注意的是列表顺序必须和类成员变量声明顺序一致。
class Entity
{
public:
string m_Name;
int m_Score;
public:
// 使用成员初始化列表
Entity()
: m_Name("Unknow"), m_Score(0) // 冒号后面接列表
{
// balabala
cout<< m_Name << endl;
cout<< m_Score << endl;
}
// 优于不使用成员初始化列表
// Entity()
// {
// m_Name = "Unknow";
// m_Score = 0;
// cout<< m_Name << endl;
// cout<< m_Score << endl;
// }
};
这样代码很简洁,可以把变量赋值和其他功能分开。而且有利于提升性能:
class Example
{
public:
Example()
{
cout<< "Created Entity" << endl;
}
Example(int x)
{
cout<< "Created Entity with "<< x<< "!"<< endl;
}
};
class Entity
{
public:
Example m_Example;
public:
// 成员初始化列表的方法
// 输出只有Created Entity with 8!
Entity()
: m_Example(Example(8)) //写成m_Example(8)也可以
{
}
// 不使用的话,输出为Created Entity和Created Entity with 8!
// Entity()
// {
// m_Example = Example(8);
// }
};
这里可以看到,不使用成员初始化列表的话,Example会被创建两次,相当于先用无参数构造对象,然后又用有参数构造对象并替换了之前无参数的对象,影响性能。不使用的代码等价于如下:
class Entity
{
public:
Entity()
{
// 构造两次
Example m_Example;
m_Example = Example(8);
}
};
6 析构函数
清理使用的内存,销毁(delete)或作用域结束(如函数中创建对象)时使用。比如动态申请空间后(new),需要在析构函数中释放该空间,否则这些内存空间将被一直占用,直到程序结束。动态申请的这些空间都在堆中,需要开发者自行释放。
class Entity
{
public:
Entity(){
cout<<"Create Entity"<< endl;
}
// ↓就是这玩意
~Entity(){
cout<<"Destory Entity"<< endl;
}
};
int main(){
Entity e;
}