第一章:类与对象基础
说实话,我刚学C++那会儿,对"类"这个概念挺懵的。老师讲了一堆理论,什么"类是对象的抽象"、"对象是类的实例"……听得云里雾里。后来自己动手写代码,踩了几个坑,才慢慢明白过来。
今天咱们就来聊聊类与对象的基础。我会尽量用大白话讲,结合我实际项目中的经验,帮你把这些概念理清楚。
1. 类的定义与实例化
类是什么?说白了,就是你自己定义的一种数据类型。C++内置了int、double这些类型,但它们不够用啊。比如你要描述一个"学生",有姓名、年龄、成绩……这时候就需要自己造一个类型——这就是类。
看个最简单的例子:
class Student {
// 这里写学生的属性和行为
}; // 注意这个分号,我当年漏写过无数次
定义好类之后,怎么用呢?创建对象,也叫实例化:
Student stu1; // 栈上创建
Student* stu2 = new Student(); // 堆上创建
我个人习惯,小对象用栈,大对象或者需要控制生命周期的用堆。不过要记得,new出来的对象必须delete,否则内存泄漏——嗯,这个坑我踩过不止一次。
2. 访问修饰符:public、private、protected
这三个关键字,决定了类成员的"可见范围"。我刚开始总记混,后来用一句话就记住了:公开的随便用,私有的自己用,保护的给儿子用。
| 修饰符 | 本类 | 子类 | 外部 |
|---|---|---|---|
| public | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ❌ |
| private | ✅ | ❌ | ❌ |
核心原则:成员变量尽量设为private,通过public的成员函数来访问。这叫"封装",是面向对象的基石。
我在项目中见过有人把所有成员都写成public,图省事。结果后来需求一变,到处都要改,那叫一个痛苦。你想想看,如果外部代码直接操作你的数据,你连改个变量名都得小心翼翼。
3. 成员变量与成员函数
成员变量就是类的属性,成员函数就是类的行为。举个例子:
class Student {
private:
string name; // 成员变量
int age;
float score;
public:
void setName(string n) { name = n; } // 成员函数
string getName() { return name; }
};
这里name、age、score是成员变量,setName、getName是成员函数。注意看,我习惯把成员变量放在private里,然后通过public的getter/setter来访问。这样做的好处是:将来如果想加个校验逻辑,比如年龄不能为负,直接在setter里改就行,外部代码不用动。
小技巧:成员变量命名时,我习惯加个前缀m_,比如m_name、m_age。这样一眼就能看出是成员变量,不会和局部变量搞混。当然也有人用后缀下划线,看团队规范。
4. 构造函数与析构函数
构造函数,就是对象创建时自动调用的函数。用来做初始化工作。析构函数,是对象销毁时自动调用的函数。用来做清理工作。
class Student {
private:
string name;
int* scores; // 动态数组
public:
// 构造函数
Student(string n, int count) {
name = n;
scores = new int[count]; // 分配内存
cout << name << " 构造完成" << endl;
}
// 析构函数
~Student() {
delete[] scores; // 释放内存
cout << name << " 析构完成" << endl;
}
};
构造函数的名字和类名一样,没有返回值。析构函数前面加个~,也没有返回值。这两个函数如果你不写,编译器会帮你生成一个默认的——但默认的啥也不干。
注意:如果你的类里有指针成员,并且你new了内存,一定要在析构函数里delete。否则内存泄漏。我曾经在一个网络库中漏写了析构函数,服务器跑了三天后内存爆了……那教训,刻骨铭心。
构造函数可以重载,也就是说可以有多个不同参数的构造函数:
class Student {
public:
Student() { name = "未知"; } // 无参构造
Student(string n) { name = n; } // 带参构造
Student(string n, int a) { // 两个参数
name = n;
age = a;
}
};
5. this指针
this是个隐藏的指针,指向当前对象本身。每个成员函数里都能用。什么时候需要显式用呢?最常见的情况——参数名和成员变量名冲突:
class Student {
private:
string name;
public:
void setName(string name) {
this->name = name; // 左边的name是成员变量,右边的name是参数
}
};
你看,参数叫name,成员变量也叫name。如果不加this->,编译器就懵了:你到底要赋值给谁?加上this->,明确告诉它:给当前对象的name赋值。
还有一种用法,是在成员函数里返回当前对象本身:
class Student {
public:
Student& setAge(int age) {
this->age = age;
return *this; // 返回当前对象
}
};
// 这样就可以链式调用了
stu.setAge(18).setName("张三");
这种写法在C++里叫"链式编程",很多库都用这种风格。我个人觉得挺酷的,但别滥用,否则代码可读性会下降。
知识体系总览
下面这张图,帮你把本章的知识点串起来:
这张图把本章的五个核心知识点串在了一起。你可以看到,类的定义是基础,访问修饰符控制着成员的可见性,成员变量和函数构成了类的具体内容,构造和析构管理着对象的生命周期,而this指针则是成员函数里一个隐形的帮手。
好了,第一章的内容就到这里。记住一句话:类是蓝图,对象是房子。蓝图画得再好,也得动手盖起来才算数。多写代码,多踩坑,慢慢就熟练了。
公众号:蓝海资料掘金营,微信deep3321