类与对象知识总结+构造函数和析构函数 C++程序设计与算法笔记总结(二) 北京大学 郭炜(上)

简介: 类与对象知识总结+构造函数和析构函数 C++程序设计与算法笔记总结(二) 北京大学 郭炜(上)

类和对象

结构化程序设计

C语言使用结构化程序设计:

程序 = 数据结构 + 算法

程序由全局变量以及众多相互调用的函数组成。

算法以函数的形式实现,用于对数据结构进行操作。

结构化程序设计的不足:

结构化程序设计中,函数和其所操作的数据结构,没有直观的联系。

随着程序规模的增加,程序逐渐难以理解,很难一下子看出来:

某个数据结构到底有哪些函数可以对它进行操作?

某个函数到底是用来操作哪些数据结构的?

任何两个函数之间存在怎样的调用关系?

结构化程序设计没有“封装”和“隐藏”的概念。要访问某个数据结构中的某个变量,就可以直接访问,那么当该变量的定义有改动的时候,就要把所有访问该变量的语句找出来修改,十分不利于程序的维护、扩充。

难以查错,当某个数据结构的值不正确时,难以找

出到底是那个函数导致的。

重用:在编写某个程序时,发现其需要的某项功能,在现有的某个程序里已经有了相同或类似的实现,那么自然希望能够将那部分代码抽取出来,在新程序中使用。

在结构化程序设计中,随着程序规模的增大,由于程序大量函数、变量之间的关系错综复杂,要抽取这部分代码,会变得十分困难。

总之,结构化的程序,在规模庞大时,会变得难以理解,难以扩充(增加新功能),难以查错,难以重用。

软件业的目标是更快、更正确、更经济地建立软件。

• 如何更高效地实现函数的复用?

• 如何更清晰的实现变量和函数的关系?使得程序更清晰更易于修改和维护。

面向对象程序设计和面向过程程序设计的对比

下面分别给出一个简单示例展示面向对象程序设计和面向过程程序设计之间的区别:

面向对象程序设计示例(C++)

#include <iostream>
using namespace std;
class Rectangle // 定义矩形类
{
public:
    double width; // 矩形宽度
    double height; // 矩形高度
    // 计算矩形面积
    double area() { return width * height; }
    // 输出矩形属性信息
    void printInfo()
    {
        cout << "Width: " << width << endl;
        cout << "Height: " << height << endl;
        cout << "Area: " << area() << endl;
    }
};
int main()
{
    Rectangle r1; // 创建一个矩形对象
    r1.width = 2.5; // 设置矩形宽度
    r1.height = 3.7; // 设置矩形高度
    r1.printInfo(); // 输出矩形属性信息
    return 0;
}

在上述代码中,我们定义了一个Rectangle类,包括矩形的属性(宽、高)以及行为(计算面积、输出信息)。然后在主函数中,创建了一个矩形对象,并通过其成员函数实现对矩形的操作。

面向过程程序设计示例(C语言)

#include <stdio.h>
// 计算矩形面积
double area(double width, double height)
{
    return width * height;
}
// 输出矩形信息
void printInfo(double width, double height)
{
    printf("Width: %.2f\n", width);
    printf("Height: %.2f\n", height);
    printf("Area: %.2f\n", area(width, height));
}
int main()
{
    double w = 2.5; // 矩形宽度
    double h = 3.7; // 矩形高度
    printInfo(w, h); // 输出矩形属性信息
    return 0;
}

在上述代码中,我们定义了area函数和printInfo函数来计算矩形的面积和输出矩形的属性信息。然后在主函数中,通过调用这些函数来实现对矩形的操作。

从上述两个示例可以看出,面向对象程序设计注重对象的封装、抽象和继承等特性,代码清晰、易读、易于修改,对于大型程序的开发而言具有较强的优势。面向过程程序设计则更加强调算法的设计和流程控制,并希望通过简单、清晰的代码来完成某些重复性工作。

面向对象的程序设计

面向对象的程序设计方法,能够较好解决上述问题。

面向对象的程序 = 类 + 类 + …+ 类

设计程序的过程,就是设计类的过程。

面向对象的程序设计方法:

将某类客观事物共同特点(属性)归纳出来,形成一个数据

结构(可以用多个变量描述事物的属性);将这类事物所能进行的行为也归纳出来,形成一个个函数,这些函数可以用来操作数据结构(这一步叫“抽象”)。然后,通过某种语法形式,将数据结构和操作该数据结构的函数“捆绑”在一起,形成一个“类”,从而使得数据结构和操作该数据结构的算法呈现出显而易见的紧密关系,这就是“封装”。

面向对象的程序设计具有“抽象”,“封装”“继承”“多态”四个基本特点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5XOyg5g8-1685970170687)(2023-06-05-19-35-56.png)]

类和对象

在C++中,类(Class)是一种面向对象的概念,它描述了一个包含数据和方法(函数)的抽象实体,用来定义某个对象的属性和行为。类只是模板或蓝图,在创建对象时依据其定义,用于声明一个具有特定属性和功能的新数据类型。

而对象(Object)则是通过实例化一个类(可以理解为从类中生成一个具体的实例)而得到的一个真实存在的事物,拥有类所描述的属性和行为。通过操作对象的属性和行为,我们可以完成各种任务和操作。

例如,我们可以定义一个名为Rectangle的类来描述矩形,如下所示:

class Rectangle 
{
public:
    double width;
    double height;
    double area()
    {
        return width * height;
    }
};

该类中包含属性widthheight,表示矩形的长和宽,并定义了一个成员函数area()用于计算矩形的面积。现在我们可以通过实例化这个类来创建一个真正的矩形对象,如下所示:

Rectangle r; // 创建一个矩形对象
r.width = 2.5; // 设置矩形的宽度
r.height = 3.7; // 设置矩形的高度
double a = r.area(); // 调用矩形的成员函数计算矩形的面积

这里的r就是一个矩形对象,它包含了类Rectangle中定义的属性和行为(成员函数),我们可以通过直接操作这些属性来进行计算,而不必关心具体的实现方式。

C++中的类和对象提供了一种抽象和封装的机制,帮助程序员更好地管理和组织代码,并以更高效、更安全的方式进行编程和设计。

对象的内存分配

和结构变量一样,对象所占用的内存空间的大小,等于所有成员变量的大小之和。

对于上面的CRectangle类,sizeof(CRectangle) = 8

每个对象各有自己的存储空间。一个对象的某个成员变量被改变了,不会影响到另一个对象。

对象间的运算

和结构变量一样,对象之间可以用 “=”进行赋值,但是不能用 “==”,“!=”,“>”,“<”“>=”“<=”进行比较,除非这些运算符经过了“重载”。

用法1:对象名.成员名

CRectangle r1,r2;
r1.w = 5;
r2.Init(5,4); //Init函数作用在 r2 上,即Init函数执行期间访问的
//w 和 h是属于 r2 这个对象的, 执行r2.Init 不会影响到 r1。

用法2. 指针->成员名

CRectangle r1,r2;
CRectangle * p1 = & r1;
CRectangle * p2 = & r2;
p1->w = 5;
p2->Init(5,4); //Init作用在p2指向的对象上

用法3:引用名.成员名

CRectangle r2;
CRectangle & rr = r2;
rr.w = 5;
rr.Init(5,4); //rr的值变了,r2的值也变
void PrintRectangle(CRectangle & r)
{ cout << r.Area() << ","<< r.Perimeter(); }
CRectangle r3;
r3.Init(5,4); 
PrintRectangle(r3);

类成员的可访问范围

在类的定义中,用下列访问范围关键字来说明类成员可被访问的范围:

– private: 私有成员,只能在成员函数内访问
– public : 公有成员,可以在任何地方访问
– protected: 保护成员,以后再说

以上三种关键字出现的次数和先后次序都没有限制。

定义一个类
class className {
private:
私有属性和函数//说明类成员的可访问范围
public:
公有属性和函数//说明类成员的可访问范围
protected:
保护属性和函数//说明类成员的可访问范围
};

如果某个成员前面没有上述关键字,则缺省地被认为
是私有成员。

class Man {
int nAge; //私有成员
char szName[20]; // 私有成员
public:
void SetName(char * szName){
strcpy( Man::szName,szName);
} 
};

在类的成员函数内部,能够访问:当前对象的全部属性、函数;同类其它对象的全部属性、函数。

在类的成员函数以外的地方,只能够访问该类对象的公有成员

class CEmployee { 
private:
char szName[30]; //名字
public :
int salary; //工资
void setName(char * name); 
void getName(char * name);
void averageSalary(CEmployee e1,CEmployee e2);
};
void CEmployee::setName( char * name) {
strcpy( szName, name); //ok
}
void CEmployee::getName( char * name) {
strcpy( name,szName); //ok
}
void CEmployee::averageSalary(CEmployee e1,
CEmployee e2){
cout << e1.szName; //ok,访问同类其他对象私有成员
salary = (e1.salary + e2.salary )/2;
}
int main()
{
CEmployee e;
strcpy(e.szName,"Tom1234567889"); //编译错,不能访
问私有成员
e.setName( "Tom"); // ok
e.salary = 5000; //ok
return 0;
}
int main()
{
CEmployee e;
strcpy(e.szName,"Tom1234567889"); //编译错,不能访
问私有成员
e.setName( "Tom"); // ok
e.salary = 5000; //ok
return 0;

设置私有成员的机制,叫“隐藏

“隐藏”的目的是强制对成员变量的访问一定要通过成员函数进行,那么以后成员变量的类型等属性修改后,只需要更改成员函数即可。否则,所有直接访问成员变量的语句都需要修改。

如果将上面的程序移植到内存空间紧张的手持设备上,希望szName 改为 char szName[5],若szName不是私有,那么就要找出所有类似

strcpy(e.szName,“Tom1234567889”);

这样的语句进行修改,以防止数组越界。这样做很麻烦。

如果将szName变为私有,那么程序中就不可能出现(除非在类的

内部)strcpy(e.szName,“Tom1234567889”);这样的语句,所有对 szName的访问都是通过成员函数来进行,比如:

e.setName( “Tom12345678909887”);

那么,就算szName改短了,上面的语句也不需要找出来修改,只要改 setName成员函数,在里面确保不越界就可以了。

用struct定义类

struct CEmployee { 
char szName[30]; //公有!!
public :
int salary; //工资
void setName(char * name); 
void getName(char * name);
void averageSalary(CEmployee 
e1,CEmployee e2);
};
和用"class"的唯一区别,就是未说明是公有还是私有的成员,就是公有

成员函数的重载及参数缺省

成员函数的重载(Overloading)指的是在同一个类中定义多个名称相同但参数个数或参数类型不同的成员函数,以实现类似的功能但具有不同的行为。重载可以极大提高代码的复用性和可读性,在需要使用同一函数名但行为却略有不同的情况下,使用重载能够让代码更为简洁。

重载的方式具体有两种:

  1. 同名不同参:函数名称相同,但是参数个数或类型不同,如下所示:
class Rectangle
{
public:
    double width;
    double height;
    double area()
    {
        return width * height;
    }
    int area(int times)
    {
        return width * height * times;
    }
};

上述例子定义了两个area方法,其功能都是计算矩形面积。在第一个函数中,该方法不接受任何参数,返回浮点数类型的计算结果;而在第二个函数中,该方法接受一个整型参数,并将浮点数类型的面积值乘以这个参数,最后返回整型类型的计算结果。这样在调用时,可以根据不同的需求选择不同的方法来处理数据:

Rectangle r;
r.width = 2.5;
r.height = 3.7;
double a = r.area(); // 调用第一个area方法
int b = r.area(2);   // 调用第二个area方法
  1. 同名同参但类型不同:函数名称和参数完全相同,但是返回值类型不同。例如,可以有一个成员函数和一个友元函数都名为operator+(), 其形参和行为相同,只是前者的调用方式限定在该类的对象上。

成员函数也支持参数缺省(Default Arguments)的语法,允许在定义成员函数时声明某个或某些参数的默认值,而在函数调用时如果没有传递对应的参数,则使用默认值,如下所示:

class Rectangle
{
public:
    double width;
    double height;
    double area(double rate = 1.0)
    {
        return width * height * rate;
    }
};

上述代码中, double rate = 1.0 声明了一个默认参数,当调用该函数时如果没有指定rate值,则默认为1.0。这个特性广泛用于提高重载函数的可读性,增加使用方便性。

总之,通过使用重载和参数缺省,我们可以面向对象设计中实现更丰富、灵活和易用的编程风格来应对不同的运算需求。

使用缺省参数要注意避免有函数重载时的二义性

class Location {
private :
int x, y;
public:
void init( int x =0, int y = 0 );
void valueX( int val = 0) { x = val; }
int valueX() { return x; }
};
Location A;
A.valueX(); //错误,编译器无法判断调用哪个valueX
目录
相关文章
|
1月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
58 12
|
2月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
59 16
|
2月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
2月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
2月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
11天前
|
算法 数据安全/隐私保护
基于GA遗传算法的悬索桥静载试验车辆最优布载matlab仿真
本程序基于遗传算法(GA)实现悬索桥静载试验车辆最优布载的MATLAB仿真(2022A版)。目标是自动化确定车辆位置,使加载效率ηq满足0.95≤ηq≤1.05且尽量接近1,同时减少车辆数量与布载时间。核心原理通过优化模型平衡最小车辆使用与ηq接近1的目标,并考虑桥梁载荷、车辆间距等约束条件。测试结果展示布载方案的有效性,适用于悬索桥承载能力评估及性能检测场景。
|
11天前
|
算法 机器人 数据安全/隐私保护
基于双向RRT算法的三维空间最优路线规划matlab仿真
本程序基于双向RRT算法实现三维空间最优路径规划,适用于机器人在复杂环境中的路径寻找问题。通过MATLAB 2022A测试运行,结果展示完整且无水印。算法从起点和终点同时构建两棵随机树,利用随机采样、最近节点查找、扩展等步骤,使两棵树相遇以形成路径,显著提高搜索效率。相比单向RRT,双向RRT在高维或障碍物密集场景中表现更优,为机器人技术提供了有效解决方案。
|
1月前
|
存储 算法 调度
基于和声搜索优化算法的机器工作调度matlab仿真,输出甘特图
本程序基于和声搜索优化算法(Harmony Search, HS),实现机器工作调度的MATLAB仿真,输出甘特图展示调度结果。算法通过模拟音乐家即兴演奏寻找最佳和声的过程,优化任务在不同机器上的执行顺序,以最小化完成时间和最大化资源利用率为目标。程序适用于MATLAB 2022A版本,运行后无水印。核心参数包括和声记忆大小(HMS)等,适应度函数用于建模优化目标。附带完整代码与运行结果展示。
|
11天前
|
算法 JavaScript 数据安全/隐私保护
基于GA遗传优化的最优阈值计算认知异构网络(CHN)能量检测算法matlab仿真
本内容介绍了一种基于GA遗传优化的阈值计算方法在认知异构网络(CHN)中的应用。通过Matlab2022a实现算法,完整代码含中文注释与操作视频。能量检测算法用于感知主用户信号,其性能依赖检测阈值。传统固定阈值方法易受噪声影响,而GA算法通过模拟生物进化,在复杂环境中自动优化阈值,提高频谱感知准确性,增强CHN的通信效率与资源利用率。预览效果无水印,核心程序部分展示,适合研究频谱感知与优化算法的学者参考。
|
1月前
|
算法 安全 数据安全/隐私保护
基于AES的遥感图像加密算法matlab仿真
本程序基于MATLAB 2022a实现,采用AES算法对遥感图像进行加密与解密。主要步骤包括:将彩色图像灰度化并重置大小为256×256像素,通过AES的字节替换、行移位、列混合及轮密钥加等操作完成加密,随后进行解密并验证图像质量(如PSNR值)。实验结果展示了原图、加密图和解密图,分析了图像直方图、相关性及熵的变化,确保加密安全性与解密后图像质量。该方法适用于保护遥感图像中的敏感信息,在军事、环境监测等领域具有重要应用价值。

热门文章

最新文章