一文吃透C++类与对象:从基础语法到实战应用
文章目录
-
前言
-
一、核心概念:类与对象的关系(必懂)
-
二、类的定义:语法详解(入门必写)
-
三、对象的实例化与使用(实战入门)
-
四、类的核心特性:构造函数与析构函数(重中之重)
-
五、常见误区:新手必避的3个坑
-
六、实战案例:完整的类与对象代码
-
七、总结:类与对象核心要点
前言
C++作为面向对象编程(OOP)的经典语言,“类与对象”是其核心基石,也是区别于C语言(面向过程)的关键所在。很多新手入门时会被“类”“对象”“封装”“成员函数”这些概念绕晕,其实只要抓住“类是模板,对象是实例”这一核心,就能轻松打通任督二脉。今天这篇博客,从基础概念到语法细节,再到实战案例,帮你彻底掌握C++类与对象。
一、核心概念:类与对象的关系(必懂)
先举一个生活化的例子,帮你快速理解:
我们可以把“汽车”理解为一个类——它是一个抽象的模板,定义了汽车的共同属性(颜色、品牌、排量)和行为(启动、刹车、加速);而你家的那辆黑色特斯拉Model 3,就是这个类的一个对象——它是模板的具体实例,有明确的属性值(黑色、特斯拉、2.0T),也能执行具体的行为(踩油门加速、踩刹车停车)。
用专业的话总结:
-
类(Class):是一种用户自定义的数据类型,封装了数据(属性)和操作数据的函数(行为),是对象的“蓝图”或“模板”。
-
对象(Object):是类的实例化结果,是具体的、可操作的实体。一个类可以实例化出无数个对象,每个对象拥有独立的属性值,但共享类的行为(成员函数)。
一句话记住:类是抽象的,对象是具体的;类是模板,对象是模板造出来的“产品”。
二、类的定义:语法详解(入门必写)
C++中,类的定义使用class关键字,语法结构清晰,主要包含三部分:访问权限修饰符、成员属性、成员函数。
1. 基本语法格式
class 类名 {
// 访问权限修饰符:public(公有)、private(私有)、protected(保护)
public:
// 成员属性(数据):类的特征
数据类型 属性名1;
数据类型 属性名2;
// 成员函数(行为):操作属性的函数
返回值类型 函数名1(参数列表) {
// 函数体:实现具体功能
}
返回值类型 函数名2(参数列表); // 声明,实现可以写在类外
private:
// 私有属性/函数:只能在类内部访问
数据类型 私有属性;
}; // 注意:类定义结尾必须加分号
2. 访问权限修饰符(重点)
C++通过访问权限修饰符,实现“封装”特性——隐藏类的内部细节,只对外提供可访问的接口,提升代码安全性和可维护性。三种修饰符的区别如下:
|
修饰符 |
访问范围 |
常用场景 |
|---|---|---|
|
public(公有) |
类内部、类外部、子类都可访问 |
成员函数(对外接口)、需要外部访问的属性 |
|
private(私有) |
仅类内部可访问 |
成员属性(隐藏细节)、内部辅助函数 |
|
protected(保护) |
类内部、子类可访问,类外部不可访问 |
继承场景中,子类需要访问的父类属性/函数 |
注意:如果不写访问权限修饰符,类的默认访问权限是 private(结构体struct默认是public)。
3. 类成员函数的两种实现方式
成员函数的实现有两种方式,根据需求选择即可:
方式1:类内实现(inline内联函数)
class Student {
public:
// 类内实现成员函数
void showInfo() {
cout << "姓名:" << name << ",年龄:" << age << endl;
}
private:
string name;
int age;
};
方式2:类外实现(需加作用域解析符::)
函数声明写在类内,实现写在类外,需要使用 类名::函数名 指明函数属于哪个类,避免命名冲突。
#include <iostream>
#include <string>
using namespace std;
// 类的声明
class Student {
public:
void showInfo(); // 声明成员函数
private:
string name;
int age;
};
// 类外实现成员函数,::是作用域解析符
void Student::showInfo() {
cout << "姓名:" << name << ",年龄:" << age << endl;
}
三、对象的实例化与使用(实战入门)
类定义完成后,需要实例化对象才能使用类的属性和成员函数。对象的实例化有两种方式:栈上实例化、堆上实例化(和内存管理结合,重点掌握)。
1. 栈上实例化(常用,自动管理生命周期)
语法:类名 对象名;,和定义普通变量类似,生命周期由编译器自动管理(函数结束后自动销毁)。
int main() {
// 栈上实例化对象
Student stu;
// 访问对象的公有成员(私有成员无法直接访问)
// 给公有属性赋值(若属性是private,需通过成员函数赋值)
stu.name = "张三"; // 错误:name是private,外部不可访问
stu.showInfo(); // 正确:showInfo是public,可外部调用
return 0;
}
2. 堆上实例化(动态分配,手动管理生命周期)
语法:类名* 对象指针 = new 类名();,和动态内存分配结合,对象在堆区,需手动用delete释放,否则会内存泄漏。
int main() {
// 堆上实例化对象,返回指针
Student* pStu = new Student();
// 通过指针访问成员:-> 运算符
pStu->showInfo();
// 手动释放内存,避免泄漏
delete pStu;
pStu = NULL; // 避免野指针
return 0;
}
3. 访问对象成员的两种方式
-
对象名访问:
对象名.成员属性/成员函数(栈上对象常用); -
指针访问:
对象指针->成员属性/成员函数(堆上对象常用)。
四、类的核心特性:构造函数与析构函数(重中之重)
构造函数和析构函数是类的特殊成员函数,无需手动调用,由编译器自动执行,负责对象的初始化和清理工作,是面向对象编程的“灵魂”。
1. 构造函数(初始化对象)
作用:对象实例化时,自动调用,用于初始化对象的成员属性(避免属性是随机值)。
核心特点:
-
函数名和类名完全一致,无返回值(甚至不能写void);
-
可以重载(多个构造函数,参数列表不同);
-
若未手动定义,编译器会自动生成一个“默认构造函数”(无参数、无函数体);
-
常用场景:给成员属性赋初始值。
示例:
class Student {
public:
// 1. 默认构造函数(无参数)
Student() {
name = "未知";
age = 0;
}
// 2. 带参数的构造函数(重载)
Student(string n, int a) {
name = n;
age = a;
}
// 3. 拷贝构造函数(用一个对象初始化另一个对象)
Student(const Student& stu) {
name = stu.name;
age = stu.age;
}
void showInfo() {
cout << "姓名:" << name << ",年龄:" << age << endl;
}
private:
string name;
int age;
};
int main() {
Student stu1; // 调用默认构造函数
Student stu2("张三", 18); // 调用带参数构造函数
Student stu3 = stu2; // 调用拷贝构造函数
stu1.showInfo(); // 输出:姓名:未知,年龄:0
stu2.showInfo(); // 输出:姓名:张三,年龄:18
stu3.showInfo(); // 输出:姓名:张三,年龄:18
return 0;
}
2. 析构函数(清理对象)
作用:对象销毁时(生命周期结束),自动调用,用于清理对象占用的资源(如堆内存、文件句柄等)。
核心特点:
-
函数名是
~类名,无返回值,无参数(不能重载); -
若未手动定义,编译器会自动生成默认析构函数(无函数体);
-
重点:若对象有堆区资源(如new分配的内存),必须手动定义析构函数,释放堆内存,否则会内存泄漏。
示例(带堆内存的析构函数):
class Student {
public:
// 构造函数:在堆区分配内存
Student(string n, int a) {
name = new string(n); // 堆区分配字符串
age = a;
}
// 析构函数:释放堆区内存
~Student() {
if (name != NULL) {
delete name; // 释放堆区内存
name = NULL; // 避免野指针
}
cout << "析构函数调用,释放资源" << endl;
}
void showInfo() {
cout << "姓名:" << *name << ",年龄:" << age << endl;
}
private:
string* name; // 指针,指向堆区内存
int age;
};
int main() {
Student* pStu = new Student("张三", 18);
pStu->showInfo();
delete pStu; // 手动释放对象,会调用析构函数
return 0;
}
五、常见误区:新手必避的3个坑
误区1:混淆类和对象
错误认知:类可以直接使用成员属性/函数;正确做法:类是模板,必须实例化对象后,通过对象才能访问成员。
误区2:私有成员可以直接访问
错误认知:对象可以直接修改private属性;正确做法:私有成员只能在类内部访问,外部需通过公有成员函数(setter/getter)操作。
// 正确的setter/getter写法
class Student {
public:
// setter:设置私有属性值
void setName(string n) {
name = n;
}
// getter:获取私有属性值
string getName() {
return name;
}
private:
string name;
};
int main() {
Student stu;
stu.setName("张三"); // 正确:通过setter设置
cout << stu.getName(); // 正确:通过getter获取
return 0;
}
误区3:忘记释放堆上的对象
错误做法:new实例化对象后,不delete;正确做法:堆上对象必须手动delete,否则会导致内存泄漏,尤其在循环、分支中,要确保每个new都有对应的delete。
六、实战案例:完整的类与对象代码
结合以上知识点,写一个完整的“学生类”案例,涵盖类定义、构造/析构、对象使用、setter/getter:
#include <iostream>
#include <string>
using namespace std;
// 学生类定义
class Student {
public:
// 构造函数(重载)
Student() {
name = "未知";
age = 0;
score = 0.0;
}
Student(string n, int a, double s) {
name = n;
age = a;
score = s;
}
// 拷贝构造函数
Student(const Student& stu) {
name = stu.name;
age = stu.age;
score = stu.score;
}
// 析构函数
~Student() {
cout << name << "的对象已销毁" << endl;
}
// setter/getter函数
void setName(string n) { name = n; }
string getName() { return name; }
void setAge(int a) { age = a; }
int getAge() { return age; }
void setScore(double s) { score = s; }
double getScore() { return score; }
// 成员函数:展示学生信息
void showInfo() {
cout << "姓名:" << name << ",年龄:" << age << ",成绩:" << score << endl;
}
// 成员函数:判断成绩是否及格
bool isPass() {
return score >= 60;
}
private:
// 私有成员属性
string name;
int age;
double score;
};
// 主函数:测试类与对象
int main() {
// 栈上实例化对象
Student stu1("张三", 18, 90.5);
stu1.showInfo();
cout << "是否及格:" << (stu1.isPass() ? "是" : "否") << endl;
// 堆上实例化对象
Student* pStu2 = new Student("李四", 17, 58.0);
pStu2->showInfo();
cout << "是否及格:" << (pStu2->isPass() ? "是" : "否") << endl;
// 拷贝对象
Student stu3 = stu1;
stu3.setName("王五");
stu3.setScore(85.0);
stu3.showInfo();
// 释放堆上对象
delete pStu2;
pStu2 = NULL;
return 0;
}
运行结果:
姓名:张三,年龄:18,成绩:90.5
是否及格:是
姓名:李四,年龄:17,成绩:58.0
是否及格:否
姓名:王五,年龄:18,成绩:85.0
李四的对象已销毁
王五的对象已销毁
张三的对象已销毁
注意:栈上对象的销毁顺序和实例化顺序相反(先实例化的后销毁),堆上对象手动delete后立即销毁。
七、总结:类与对象核心要点
1. 核心关系:类是模板,对象是实例;类抽象,对象具体;
2. 类的结构:访问权限(public/private/protected)+ 成员属性 + 成员函数;
3. 对象实例化:栈上(自动管理)、堆上(手动delete);
4. 特殊函数:构造函数(初始化,可重载)、析构函数(清理,不可重载);
5. 核心原则:封装(隐藏私有细节,提供公有接口),提升代码安全性和可维护性。
更多推荐


所有评论(0)