往篇内容:
C++学习笔记(一)
一、C++编译阶段※二、入门案例解析
三、命名空间详解
四、C++程序结构
C++学习笔记(二)
五、函数基础
六、标识符
七、数据类型
补充:二进制相关的概念
sizeof 运算符简介
补充:原码、反码和补码
C++学习笔记(三)
补充:ASCII码表
八、内存构成
补充:变量
九、指针基础
十、const关键字
十一、枚举类型
C++学习笔记(四)
十二、 类型转换
十三、define指令
十四、typedef关键字
十五、运算符
十六、 流程控制
C++学习笔记(六)
十七、数组
C++学习笔记(七)
十八、指针
C++学习笔记(八)
十九、函数进阶
二十、变量进阶
C++学习笔记(九)
二十一、结构体
二十二、联合
目录
二十三、类与对象
1、面向过程
2、面向对象
3、类
4、对象
5、成员引用
6、成员函数
7、inline函数
8、对象内存
9、this指针
10、类与结构体的区别
11、封装特性
二十三、类与对象
1、面向过程
面向过程程序设计(Procedural-Oriented Programming, POP)是一种以“过程”为中心的编程思想。它强调的是解决问题的步骤和流程,通过函数(或称为过程)来组织代码。
在 C++ 中,虽然支持 OOP,但仍然兼容传统的面向过程编程方式,例如使用全局变量、函数等。
思想:“先做什么,再做什么”,即按照顺序执行一系列操作来完成任务。
特点:
特性 | 描述 |
---|---|
程序结构 | 由主函数调用多个函数组成 |
数据与行为分离 | 数据和操作数据的函数是分开的 |
顺序执行为主 | 强调流程控制,按步骤执行 |
全局变量 | 常用于共享数据 |
函数重用 | 可以通过函数复用代码逻辑 |
示例:
#include <iostream>
using namespace std;void add(int a, int b) {cout << "Sum: " << a + b << endl;
}int main() {int x = 5, y = 10;add(x, y); // 调用函数return 0;
}
2、面向对象
面向对象程序设计(Object-Oriented Programming, OOP)是一种以“对象”为核心的编程思想。它将现实世界中的事物抽象为对象,每个对象包含数据(属性)和对数据的操作(方法)。
C++ 是一种多范式语言,其中最强大的特性就是对 OOP 的完整支持。
思想:“万物皆对象”,程序由一组相互协作的对象构成,每个对象封装了自身的状态和行为
四大核心特性:
概念 | 描述 |
---|---|
封装(Encapsulation) | 将数据和操作封装在一个类中,对外隐藏实现细节 |
继承(Inheritance) | 子类可以继承父类的属性和方法,实现代码复用 |
多态(Polymorphism) | 同一个接口可以有多种实现,运行时决定具体行为 |
抽象(Abstraction) | 提取共性特征,忽略复杂实现细节 |
示例:
#include <iostream>
using namespace std;class Rectangle {
private:int width, height;public:Rectangle(int w, int h) : width(w), height(h) {}int area() {return width * height;}
};int main() {Rectangle rect(5, 10);cout << "Area: " << rect.area() << endl;return 0;
}
3、类
类(class) 是用户自定义的数据类型,用于封装数据(属性)和操作这些数据的方法(行为)。它是面向对象编程(OOP)的核心概念之一。
你可以把类看作是“蓝图”或“模板”,而对象则是根据这个模板创建出来的具体实例。
定义格式:
class 类名{
成员访问限定符: //private,public,protected三种取值
[数据成员s];
[成员函数s];
};
成员访问限定符:用来指定个数据成员的访问属性
- public :类内类外都可以访问
- private :只能被本类的成员函数引用,类外不能调用(特殊情况后面再讨论)
- protected :和private类似,派生时情况不同
注意事项:
- 成员访问限定符可以出现多次
- 作用范围:从当前限定符到下一个限定符或到类的 }
示例:
class Student {
private:
string name;
int age;
public:
void setName(string n) { name = n; }
void setAge(int a) { age = a; }
void printInfo() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
注意事项:
- 声明或定义类类型时不分配内存空间,用类类型实例创建对象时,分配内存空间
- 不指定成员限定符,默认为 private
- 定义类对象的方式有3种,与结构体类似
- 类的成员函数只能由类的对象或指针调用(特殊情况后续讨论)
4、对象
- 客观世界中任何事物都可以看成对象,对象由属性和方法组成,每个对象都具有静态的属性和动态的功能。
- 具有相同属性和功能的对象,我们可以把它们归为一类,故而我们可以得出结论:类是对象的抽象,对象是类的实例。
抽象概念理解:抽象的过程是将有关事物的共性归纳、集中的过程。
定义对象格式:
class 类名 对象名;
类名 对象名; //推荐
//定义类的同时实例化对象
class 类名{
数据成员s;
成员函数s;
}对象1,对象2...对象n;
//定义匿名类,同时实例化对象
class {
数据成员s;
成员函数s;
}对象1,对象2...对象n;
案例:
#include <iostream>
#include <cstring>
using namespace std;// 定义类类型
class Student {// 默认权限修饰符为 private// 成员函数,一般 public
public:void disp() {cout << "id: " << id << endl;cout << "name: " << name << endl;cout << "age: " << age << endl;}// 权限修饰符,一般数据成员 private// private:
public:int id;char name[20];int age;
} s3 = {1020, "lucy", 21}; // 列表初始化依旧有用int main() {// 由类类型实例化对象【定义变量】Student s1;s1.id = 1001;// s1.name = "tom"; error,详见字符串数组strcpy(s1.name, "tom");s1.age = 20;// 类对象调用成员函数Student s2 = s1; // 修正:删除了多余的class关键字s2.disp();cout << "--------------" << endl;s3.disp();return 0;
}
注意:对象初始化可以采用列表的方式进行,数据成员不能为private。
5、成员引用
使用类对象调用数据成员或成员函数有2种方式:
- 对象名.成员名;
- 类指针变量->成员名;
案例:
#include <iostream>
using namespace std;// 自定义结构体类型
class Student {
public:int num;char name[20];char sex;int age;float score;char addr[30];public:// 成员函数必须通过类对象或指针来调用// 对象名.disp(); 指针变量->disp();void disp() {cout << "Student: [" << num << "," << name << "," << sex<< "," << age << "," << score << "," << addr << "]" << endl;}
};// 值传递
void printStudent(Student s) {cout << "Student: " << s.num << "," << s.name << "," << s.sex << "," << s.age << "," << s.score << "," << s.addr << endl;
}// 指针传递
void printStudent(Student *p) {cout << "Student: {" << p->num << "," << p->name << "," << p->sex << "," << p->age << "," << p->score << "," << p->addr << "}" << endl;
}// 引用传递(注意:不能与值传递同名,会产生二义性)
void printStudent2(Student &s) {cout << "Student: (" << s.num << "," << s.name << "," << s.sex << "," << s.age << "," << s.score << "," << s.addr << ")"<< endl;
}int main() {// 初始化Student s = {2001, "Tom", 'M', 19, 78.5, "平遥学院路56号"};printStudent(s); // 值传递printStudent2(s); // 引用传递Student *ps = &s;printStudent(ps); // 指针传递cout << "---------------------" << endl;s.disp(); // 成员函数调用return 0;
}
6、成员函数
类的成员函数(member function) 是类的重要组成部分之一。它们定义了类的行为,即对象可以执行的操作。
它的用法和作用和之前学习的函数基本上是一样的,也有返回值和函数类型,它与一般函数的区别只是: 它是属于一个类的成员,出现在类体中。
在使用类函数时,要注意调用它的权限 (它能否被调用 )以及它的作用域 (函数能使用什么范围中的数据和函数 )。
成员函数定义方式
① 在类内定义(隐式内联)
class Circle {
private:
double radius;
public:
void setRadius(double r) { radius = r; } // 类内定义
double getArea() { return 3.14159 * radius * radius; }
};
⚠️ 这种方式适合简单函数,编译器会尝试将其优化为内联函数 (inline)。
② 在类外定义(推荐)
class Circle {
private:
double radius;
public:
void setRadius(double r); // 函数声明
double getArea();
};
// 类外实现
// 返回值类型 类名::函数名(参数列表);
void Circle::setRadius(double r) {
radius = r;
}
double Circle::getArea() {
return 3.14159 * radius * radius;
}
✅ 推荐这种方式:代码更清晰、结构更合理,便于维护。
注意:
如果函数名前面既无类名又无 ∷ ,如 ∷display() 或 display() ,这表示 display 函数不属于任何类,这个函数不是成员函数,而是全局函数,即非成员函数的一般普通函数。
③ 成员函数的调用
成员函数可以通过对象或指针来调用:
Circle c;
c.setRadius(5.0); // 通过对象调用
c.printInfo();
Circle* pc = &c;
pc->setRadius(10.0); // 通过指针调用
④ const成员函数
class Rectangle {
private:
int width, height;
public:
int area() const {
return width * height; // 不允许修改成员变量
}
//获取周长
int perimeter() const;
};
//类外定义const函数,一定要跟上const修饰
int Rectangle::perimeter() const
{
//width = 2; error
return 2 * (width + height);
}
🔒 注意:const 成员函数只能访问其他 const 成员函数,不能修改任何数据成员。
7、inline函数
为了减少时间开销,如果在类体中定义的成员函数中不包括循环等控制结构,C++系统会自动将它们作为内联 inline 函数来处理。
class Circle {
private:
double radius;
public:
//类内定义,即使没有inline声明,默认也为inline函数
void setRadius(double r) { radius = r; }
double getArea() { return 3.14159 * radius * radius; }
};
在调用 setRadius、getArea 时,并不真正执行函数调用过程 ,而是将函数代码嵌入程序的调用点,大大减少了调用成员函数的时间开销。
注意1:
如果成员函数在类体外定义**,系统并不把它默认为 inline 函数。
注意2:
如果要在类体外定义 inline 函数,需要在函数定义前加上 inline ,还应将类定义和成员函数的定义都放在同一个头文件中 (或者写在同一个源文件中),否则编译时无法进行代码嵌入。例如:
//函数定义时加inline,声明时则不需要
inline void Circle∷setRadius(double r)
{
radius = r;
}
弊端:上述做法,不利于类的接口与类的实现分离,不利于信息隐蔽。虽然程序的执行效率提高了,但从软件工程质量的角度来看,这样做并不是好的办法。
8、对象内存
用类去定义对象时,系统会为每一个对象分配存储空间。每个对象占用内存空间大小,只取决于数据成员和对齐方式,和成员函数没有任何关系。
案例:
#include <iostream>
#include <cstddef> // for alignof
using namespace std;class MyClass {
public:int x;double y;char z;void disp() {cout << "x: " << x << ", y: " << y << ", z: " << z << endl;}
};int main() {cout << "Alignment of MyClass: " << alignof(MyClass) << " bytes" << endl;cout << "Size of MyClass: " << sizeof(MyClass) << " bytes" << endl;MyClass obj;obj.x = 10;obj.y = 3.14;obj.z = 'A';obj.disp();MyClass obj2;obj2.x = 3;obj2.y = 2.5;obj2.z = 'x';obj2.disp();return 0;
}
思考:为什么调用不同对象成员函数时,执行完全相同的一段函数代码,但是执行结果是不相同的?答:因为成员函数可以访问和操作对象的实例数据(属性)。每个对象的属性值是独立存储的,因此即使函数代码相同,执行时基于不同的属性值会产生不同的结果。
9、this指针
每个非静态成员函数都有一个隐藏参数: this ,它是一个指向当前对象的指针。
- this 是指向调用该函数的对象的指针
- 常用于解决形参与成员变量同名的问题
- 可以用来返回当前对象(常用于链式调用)
案例:
#include <iostream>
#include <string> // 补充缺失的头文件
using namespace std;class MyClass {
public:string name;int x;double y;char z;void disp() {// this指向成员函数的调用者cout << "this: " << this << endl;cout << "name: " << this->name << ", x: " << this->x;cout << ", y: " << this->y << ", z: " << this->z << endl;}void setName(const string& name) {this->name = name; // 也可以区分同名变量}
};int main() {MyClass c1 = {"zs", 10, 3.14, 'M'};cout << "&c1: " << &c1 << endl;c1.disp();cout << "--------------" << endl;MyClass c2 = {"jack", 20, 2.4, 'F'};cout << "&c2: " << &c2 << endl;c2.disp();return 0;
}
10、类与结构体的区别
C++增加了class类型后,仍保留了结构体类型(struct), 它不是简单地继承 C 的结构体,而是使它也具有类的特点,以便于用于面向对象程序设计。用struct声明的结构体类型实际上也就是类,两者区别如下:
struct的缺省作用域为public
class的缺省作用域为private
11、封装特性
类背后蕴含的思想是数据抽象和封装,两个重要优点:
- 避免类内部出现无意的、可能破坏对象状态的用户级错误
- 随时间推移根据需求改变或缺陷报告来完善类实现,而不需要改变用户级代码
C++通过类实现数据的封装,将相关的数据与对数据的操作封装到一个类中。
类里面的数据和成员函数通过成员限定符的修饰,改变了这些成员的访问属性,实现了信息的隐蔽。
在修改代码时,具体修改某个类,不会对别的类产生影响,方便调试检查错误,这对于大型项目尤为重要。
在C++中,封装通常通过以下方式实现:
- 使用 private 或 protected 访问修饰符来限制对类成员的直接访问
- 提供公共方法( getter 和 setter )来间接访问或修改私有成员变量
案例:
class BankAccount {
private:double balance; // 私有成员变量,外部无法直接访问public:// 构造函数BankAccount(double initialBalance) : balance(initialBalance) {}// Getter 方法double getBalance() const {return balance;}// 存款方法void deposit(double amount) {if (amount > 0) {balance += amount;}}// 取款方法bool withdraw(double amount) {if (balance >= amount && amount > 0) {balance -= amount;return true;}return false;} // 修正:将分号移至此处
}; // 修正:类定义结束符
在这个例子中, balance 变量是私有的,外界不能直接访问它。相反,提供了 getBalance , deposit 和 withdraw 方法作为与 balance 交互的接口。