编程导论(2024-2025冬季学期)
第8章:面向对象编程
课程信息
- 授课教师:亚历山大·克劳斯博士(Dr.-Ing. Alexander Krause)
- 所属机构:多特蒙德工业大学(Technische Universität Dortmund)应用信息学12系系统软件工作组
- 课程链接:https://sys.cs.tu-dortmund.de/de/lehre/ws24/eidp/
- 联系邮箱:eidp-problems@ls12.cs.tu-dortmund.de
第8章核心内容
- 面向对象编程导论
- 属性与方法
- 补充知识:宏(包含保护,include guards)
- 构造函数与析构函数
- 模板
1. 类:导论与背景
1.1 已学数据类型回顾
- 基本数据类型:char(字符型)、int(整型)、double(双精度浮点型)
- 复合数据类型:
- 数组(Array)、字符串(Zeichenkette)
- 结构体(struct),示例如下: ```cpp struct Punkt{ // 定义结构体“点” double x, y; // 坐标x和y,数据类型为double };
int main() {
Punkt p {11.2, 23.5}; // 创建结构体对象p并初始化
cout << "(" << p.x << "," << p.y << ")" << endl; // 输出坐标(11.2,23.5)
return 0;
}
``
- 已接触的类:string(字符串类)、vector(向量类),例如通过s.length()可获取字符串s`的长度。
1.2 面向对象视角下的结构体
以struct Punkt为例,从面向对象角度分析:
- 对象p:是结构体Punkt的实例(具体表现)
- 结构体Punkt:描述“点”的属性(特征)
- 该结构体的所有对象都拥有相同属性(此处为x和y,均为double类型)
- 访问属性方式:通过对象名+属性名,如p.x、p.y
- 结构体与C++类的本质区别:
- 结构体的所有属性默认可见且可直接访问
- C++类的所有属性默认隐藏且不可直接访问
2. C++类的定义与访问控制
2.1 类与结构体的等价定义
// 结构体定义
struct Punkt{
double x, y; // 默认public(公开)
};
// 等价的类定义
class Punkt{
public: // 显式声明后续成员为public
double x, y;
};
2.2 类的关键关键字
class:定义类的关键字public:声明后续成员(属性/方法)可在类外部访问;若省略,类成员默认private(私有)private:声明后续成员仅能在类内部访问,外部无法直接操作- 结构体与类的访问权限对比: | 类型 | 默认访问权限 | 显式声明private后的效果 | |------------|--------------|--------------------------------| | struct | public | 成员仅类内部可访问 | | class | private | 成员仅类内部可访问(与struct一致) |
示例:三种等价的私有属性定义
// 1. 结构体+private
struct Punkt{
private:
double x, y;
};
// 2. 类+private
class Punkt{
private:
double x, y;
};
// 3. 类+默认private(省略private关键字)
class Punkt{
double x, y;
};
```1-45、1-49、1-50、1-51、1-52、1-53、1-54
### 2.3 类的访问控制示例
```cpp
// 类定义:x和y默认private
class Punkt{
double x, y;
};
// 尝试访问私有属性——编译失败
Punkt p {11.2, 23.5}; // 错误:无法直接初始化私有属性
p.x = 1.1; p.y = 2.2; // 错误:私有属性禁止外部直接修改
- 核心原因:
x和y为私有属性,外部直接访问被禁止,这是有意设计的安全机制 - 解决方案:通过类的方法(成员函数) 间接访问私有属性,且方法可实现数据合法性校验(如范围检查)
- 结构体的访问示例(对比):
struct Punkt{
double x, y; // 默认public
};
// 直接访问公开属性——编译通过
Punkt p {11.2, 23.5};
p.x = 1.1; p.y = 2.2; // 合法,可直接修改
```1-57、1-58、1-59、1-60、1-61、1-62、1-63、1-64、1-65、1-66、1-67、1-68、1-69、1-70
## 3. 类的方法(成员函数)
### 3.1 过程式编程与面向对象编程的方法对比
#### 过程式编程(结构体+外部函数)
```cpp
// 结构体定义
struct Punkt{
double x, y;
};
// 外部函数:修改x坐标
void setzeX(Punkt &p, double wx){
p.x = wx;
}
// 外部函数:读取x坐标
double leseX(Punkt &p){
return p.x;
}
// 外部函数:修改y坐标
void setzeY(Punkt &p, double wy){
p.y = wy;
}
// 外部函数:读取y坐标
double leseY(Punkt &p){
return p.y;
}
// 调用示例
int main() {
Punkt p {11.2, 23.5};
setzeX(p, 22.3); // 需传入结构体对象
cout << leseX(p) << endl; // 输出22.3
return 0;
}
面向对象编程(类+成员函数)
// 类定义
class Punkt{
private:
double x, y; // 私有属性
public:
// Setter方法(设置器):修改x坐标
void setzeX(double wx){
x = wx; // 类内部可直接访问私有属性
}
// Setter方法:修改y坐标
void setzeY(double wy){
y = wy;
}
// Getter方法(获取器):读取x坐标
double leseX() {
return x;
}
// Getter方法:读取y坐标
double leseY() {
return y;
}
};
// 调用示例
int main() {
Punkt p; // 创建类对象p
p.setzeX(22.3); // 通过对象调用成员方法
cout << p.leseX() << endl; // 输出22.3
return 0;
}
3.2 方法的优势:数据校验
通过方法可对输入数据进行合法性检查,避免非法值赋值:
void setzeX(double wx){
if (wx < 0) // 校验:若x坐标为负数,强制设为0.0
x = 0.0;
else
x = wx;
}
```1-73、1-74、1-75、1-76、1-77、1-78、1-79、1-80、1-81、1-82、1-83、1-84、1-87、1-88、1-89、1-90、1-91、1-92、1-93
## 4. 面向对象的核心概念
### 4.1 类(Klasse)
- 定义:描述一组对象的**属性(成员变量)** 和**操作(成员方法)** 的集合
- 本质:创建对象的“蓝图”(设计模板),而非对象本身
- 特性:属于一种**数据类型**,可用于定义变量(对象)
### 4.2 对象(Objekt)
- 定义:类的**实例**(具体实现),拥有具体数据
- 特性:占用内存空间、具有唯一标识
- 访问规则:仅能通过类的方法访问内部数据(“向对象发送消息”),格式为`对象名.方法名(参数)`
### 4.3 方法(Methode)
- 定义:作用于类对象的函数,描述对象的“行为”
- 别名:成员函数、元素函数、类函数
- 调用:需通过对象或类(静态方法)调用
### 4.4 属性(Attribute)
- 定义:对象的数据/特征,也称为成员变量
- 命名规范:通常用前缀`m_`标识(如`m_x`、`m_y`),区分普通变量
示例:类与对象的使用
```cpp
class Punkt{
private:
double x, y; // 属性
public:
void setzeX(double wx){ x = wx; } // 方法
// ...其他方法
};
// 创建3个Punkt类的对象
Punkt p1, p2, p3;
// 调用对象的方法
p1.setzeX(22.3);
p2.setzeY(23.5);
p3.leseX();
```1-95、1-96、1-97、1-98、1-99、1-100、1-101、1-102、1-103、1-104、1-105、1-108、1-109、1-110、1-111、1-112、1-113、1-114、1-115、1-116、1-117、1-118、1-119、1-120、1-121、1-122、1-123、1-124
## 5. 类的设计目标
面向对象编程(通过类)的核心目标的包括:
1. **数据封装**:将属性(数据)与方法(操作)封装在类内部,隐藏实现细节
2. **代码复用**:类可作为独立模块重复使用,降低开发成本
3. **继承**:子类可继承父类的属性和方法(详见第10章)
4. **虚方法**:支持多态特性,子类可重写父类方法(详见第11章)
5. **软件设计基础**:为复杂软件系统提供模块化、清晰的设计思路1-126、1-127、1-128、1-129、1-130、1-131、1-132、1-133、1-134、1-135、1-136、1-137
## 6. 面向对象的问题建模思路
### 6.1 建模逻辑
应用问题 ⇒ 建模 ⇒ 提炼“核心要素”(与问题相关的关键信息)
- 注:“核心要素”取决于问题视角,同一问题可能有多种建模方式
- 分析阶段需围绕“对象、对象属性、对象间关系”展开思考
### 6.2 面向对象编程(OOP)的本质
- 用现实世界的概念(而非计算机技术概念)构建模型
- 避免过早陷入“主程序/子程序”等技术细节
- 模型核心:包含属性和方法的类(对象),以及对象间的交互1-140、1-141、1-142、1-143、1-144、1-145、1-146、1-147、1-148
## 7. 类的定义与实现分离(信息隐藏)
### 7.1 核心原则
- **信息隐藏**:分离“类定义(接口)”与“方法实现(内部逻辑)”
- 文件分工:
- 头文件(*.h):存放类定义(对外公开的接口)
- 源文件(*.cpp):存放方法实现(对外隐藏的内部逻辑)
### 7.2 文件结构示例
#### 头文件(Punkt.h):类定义
```cpp
// 包含保护(避免头文件重复包含)
#ifndef PUNKT_H
#define PUNKT_H
class Punkt{
private:
double x, y; // 私有属性(隐藏)
public:
// 方法声明(公开接口)
void setzeX(double wx);
void setzeY(double wy);
double leseX() const; // const:调用不修改对象状态
double leseY() const;
void verschiebe(double wx, double wy);
void gleich(Punkt const &p);
};
#endif // PUNKT_H
源文件(Punkt.cpp):方法实现
// 包含头文件(获取类定义)
#include "Punkt.h"
// 实现setzeX方法:类名::方法名
void Punkt::setzeX(double wx){
x = wx;
}
// 实现setzeY方法
void Punkt::setzeY(double wy){
y = wy;
}
// 实现leseX方法
double Punkt::leseX() const {
return x;
}
// 实现leseY方法
double Punkt::leseY() const {
return y;
}
// 实现verschiebe方法(平移:x增加wx,y增加wy)
void Punkt::verschiebe(double wx, double wy){
x += wx;
y += wy;
}
7.3 const方法的作用
- 确保方法调用时不修改对象的任何属性
- 仅
const方法可被const对象调用,非const方法不可调用:
Punkt const p; // const对象:属性不可修改
p.leseX(); // 合法:leseX()是const方法
p.setzeX(1.2); // 错误:setzeX()是非const方法,可能修改对象
```1-151、1-152、1-153、1-154、1-155、1-156、1-157、1-158、1-159、1-160、1-161、1-162、1-163、1-164、1-165、1-166、1-167、1-168、1-169、1-170、1-171、1-172、1-173、1-174、1-175、1-176、1-177、1-178、1-179、1-180、1-181、1-182、1-183、1-184、1-185、1-186、1-187、1-188、1-189、1-190、1-191、1-192、1-193、1-194、1-195、1-196、1-197、1-198、1-199、1-200、1-201、1-202、1-203、1-204、1-205、1-206、1-207
## 8. 补充知识:包含保护(Include Guards)
### 8.1 问题背景
若头文件被多次包含,会导致“重复定义”错误,示例:
```cpp
// fileA.h
class A{ int a; };
// fileB.h:包含fileA.h
#include "fileA.h"
class B{ int b; };
// main.cpp:包含fileA.h和fileB.h
#include "fileA.h"
#include "fileB.h"
int main(){ };
预处理后,main.cpp内容变为:
class A{ int a; }; // 来自fileA.h
class B{ int b; }; // 来自fileB.h
class A{ int a; }; // 来自fileB.h中的#include "fileA.h"——重复定义
int main(){ };
此时编译会报错(类A重复定义)。
8.2 包含保护
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com