1. 面向对象的概念
面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,通过将代码组织为对象的集合来实现程序的设计和实现。它将数据和方法封装在一起,使得代码更加模块化、可重用性更高。
1.1 面向过程与面向对象
在面向过程编程中,程序被分解为一系列的函数,通过这些函数的调用来实现程序的功能。这种编程范式注重流程控制和数据的处理,但难以实现代码的重用性和扩展性。
面向对象编程将程序分解为一系列的对象,每个对象都有自己的数据和方法,通过对象之间的交互来实现程序的功能。这种编程范式更注重数据和方法的封装,使得代码更加模块化、易于维护、可重用性更高。
1.2 面向对象程序设计的基本概念
1.2.1 类(Class)
类是面向对象编程的基本模块,它是一种数据结构的抽象,描述了对象的属性和行为。类定义了对象的模板,通过类可以创建多个相似的对象。
1.2.2 对象(Object)
对象是类的实例,代表了一个具体的事物。对象具有一些状态(属性)和行为(方法),状态通过属性表示,行为通过方法表示。
1.2.3 封装(Encapsulation)
封装是一种将数据和方法进行组合的形式,通过封装可以隐藏对象内部的实现细节,只暴露一些必要的接口供外部使用。封装提高了代码的安全性和可维护性。
1.2.4 继承(Inheritance)
继承是一种通过定义子类来继承父类的属性和方法的机制。通过继承,子类可以重用父类的代码,并且可以根据需要增加、修改或覆盖父类的行为。
1.2.5 多态(Polymorphism)
多态是一种同一个方法在不同对象上的表现形式不同的现象。通过多态,可以以统一的方式处理不同类型的对象,提高了代码的可扩展性和灵活性。
1.3 面向对象程序设计的特点
增加了代码的可重用性和模块化
提高了代码的可维护性和扩展性
使得程序的设计更加自然和直观
降低了代码的复杂性,并提高了代码的可读性和可理解性
下面是一个简单的C++代码示例,展示了如何定义和使用类:
#include <iostream> using namespace std; // 定义一个表示矩形的类 class Rectangle { private: int width; int height; public: // 构造函数 Rectangle(int w, int h) { width = w; height = h; } // 计算矩形的面积 int getArea() { return width * height; } }; int main() { // 创建一个矩形对象 Rectangle rect(4, 5); // 调用getArea方法计算矩形的面积 int area = rect.getArea(); cout << "矩形的面积为:" << area << endl; return 0; }
代码解析:
在main函数中,我们创建了一个矩形对象rect,并传入宽度4和高度5来初始化它
调用rect对象的getArea方法来计算矩形的面积
最后,将计算得到的面积输出到控制台
2.类与对象
2.1 类的定义
类(Class)是C++的核心概念之一,用于封装数据和相关操作。类定义了一个数据类型,描述了对象的属性和行为。
【例1-1】定义长方形类Rect
#include <iostream> using namespace std; class Rect { private: double length; double width; public: Rect(double l, double w) { length = l; width = w; } double getArea() { return length * width; } }; int main() { Rect rect(5.0, 3.0); cout << "The area of the rectangle is: " << rect.getArea() << endl; return 0; }
分析:该例子定义了一个长方形类Rect,私有数据成员length和width保存长和宽。公有成员函数Area()用于计算长方形的面积。在main函数中创建了一个Rect对象rect,并通过getArea()函数计算出长方形的面积。
【例1-2】使用工程管理Rect类
#include <iostream> using namespace std; class Rect { private: double length; double width; public: Rect(double l, double w) { length = l; width = w; } double getArea() { return length * width; } }; int main() { Rect rect(5.0, 3.0); cout << "The area of the rectangle is: " << rect.getArea() << endl; return 0; }
分析:该例子演示了如何使用工程管理类Rect。在main函数中创建了一个Rect对象rect,并通过getArea()函数计算出长方形的面积。
2.2 成员的访问控制
类的成员可以被定义为public、private或protected。这些访问修饰符决定了成员的可见性和访问权限。
2.3 类的成员函数
类的成员函数是类的行为的具体实现。它们可以访问类的私有成员,并提供对外部使用者的接口。
【例1-3】定义圆类Circle
#include <iostream> using namespace std; const double PI = 3.14159; class Circle { private: double radius; public: Circle(double r) { radius = r } double getArea() { return PI * radius * radius; } double getPerimeter() { return 2 * PI * radius; } }; int main() { Circle circle(5.0); cout << "The area of the circle is: " << circle.getArea() << endl; cout << "The perimeter of the circle is: " << circle.getPerimeter() << endl; return 0; }
分析:该例子定义了一个圆类Circle,私有数据成员radius保存半径。公有成员函数getArea()计算圆的面积,getPerimeter()计算圆的周长。在main函数中创建了一个Circle对象circle,并通过getArea()和getPerimeter()函数分别计算圆的面积和周长。
【例1-4】矩形对象占用的内存
#include <iostream> using namespace std; class Rectangle { private: double length; double width; public: Rectangle(double l, double w) { length = l; width = w; } }; int main() { Rectangle rect(5.0, 3.0); cout << "The size of rectangle object is: " << sizeof(rect) << " bytes" << endl; return 0; }
分析:该例子演示了矩形对象占用的内存大小。在main函数中创建了一个Rectangle对象rect,并使用sizeof函数获取对象所占的内存大小,单位为字节。
3. 构造函数
3.1 构造函数概述
构造函数是一种特殊的成员函数,用于创建对象时初始化对象的成员变量。在C++中,构造函数的名称与类的名称相同,可以有参数和返回值(返回值为void)。
【例1-5】为Rect类添加构造函数
#include<iostream> using namespace std; class Rect { private: int width; int height; public: Rect(int w, int h) { width = w; height = h; } }; int main() { Rect r(3, 4); // 调用构造函数创建Rect对象并初始化 return 0; }
上述示例中,我们为Rect类添加了一个带有两个参数的构造函数。在main函数中,我们通过传递参数来创建Rect对象,并将传入的参数值赋给对象的成员变量。
【例1-6】使用初始化列表
#include<iostream> using namespace std; class Rect { private: int width; int height; public: Rect(int w, int h) : width(w), height(h) {} }; int main() { Rect r(3, 4); return 0; }
初始化列表是在构造函数的参数列表后面使用冒号来初始化成员变量的一种方式。上述示例中,我们使用初始化列表来初始化Rect类的成员变量width和height。
3.2 默认构造函数
默认构造函数是一种无参数的构造函数。如果在类中没有定义任何构造函数,编译器将会自动生成一个默认构造函数。默认构造函数可以用于创建对象时不需要传递参数的情况。
【例1-7】提供多个构造函数
#include<iostream> using namespace std; class Rect { private: int width; int height; public: Rect() { width = 0; height = 0; } Rect(int w, int h) { width = w; height = h; } }; int main() { Rect r; // 调用默认构造函数 Rect r2(3, 4); // 调用带参数的构造函数 return 0; }
上述示例中,我们为Rect类提供了两个构造函数。其中一个是默认构造函数,没有参数,用于创建width和height都为0的对象;另一个是带有两个参数的构造函数,可以根据传入的参数值来初始化对象的成员变量。
4. 析构函数
4.1 析构函数的特征
析构函数是一种特殊的成员函数,其名称与类的名称相同,前面加上波浪符(~),没有任何参数和返回值。
在对象被销毁时,析构函数会自动调用,用于清理对象分配的资源和执行其他必要的清理操作。
析构函数可以实现自定义的清理行为,例如释放动态分配的内存、关闭文件、断开网络连接等。
4.2 析构函数的作用
析构函数的主要作用是释放对象所占用的资源,以避免内存泄漏和资源浪费。
当对象的生命周期结束时,即在对象离开其作用域、被显式删除或程序执行结束时,析构函数会被自动调用。
析构函数在对象销毁之前执行,可以用于关闭文件或网络连接、释放动态分配的内存、删除临时文件等操作。
【例1-8】使用析构函数
以下是一个使用析构函数的示例代码:
#include <iostream> class MyClass { public: MyClass() { std::cout << "构造函数被调用" << std::endl; } ~MyClass() { std::cout << "析构函数被调用" << std::endl; } }; int main() { MyClass obj; // 创建对象 // 执行其他操作 return 0; // 离开main函数作用域,对象被销毁,析构函数被调用 }
在上述代码中,我们定义了一个名为MyClass的类,其中包含一个构造函数和一个析构函数。在main函数中,我们创建了一个MyClass的对象obj。当程序执行完return语句后,main函数作用域结束,obj对象也随之销毁,这时析构函数会被自动调用。
运行此代码会输出以下结果:
构造函数被调用 析构函数被调用
这表明在对象obj被销毁时,析构函数被调用,执行了一些清理操作。这种方式确保了对象使用的资源被正确释放,避免了内存泄漏和资源浪费的问题
5. 拷贝构造函数
拷贝构造函数是一种特殊的成员函数,用于创建一个新对象并使用现有对象的值进行初始化。
在C++中,默认情况下编译器会自动生成一个浅拷贝的拷贝构造函数,即逐个复制成员变量的值。
当类中包含指针成员变量时,浅拷贝会导致多个对象共享同一块内存,容易出现问题。这时,我们需要自定义拷贝构造函数来实现深拷贝。
【例1-9】为Student类添加拷贝构造函数
以下是一个为Student类添加拷贝构造函数的示例代码:
#include <iostream> #include <cstring> class Student { public: Student(const char* name, int age) { this->name = new char[strlen(name) + 1]; strcpy(this->name, name); this->age = age; } // 拷贝构造函数 Student(const Student& other) { this->name = new char[strlen(other.name) + 1]; strcpy(this->name, other.name); this->age = other.age; } ~Student() { delete[] name; } private: char* name; int age; }; int main() { Student s1("Tom", 20); Student s2 = s1; // 使用拷贝构造函数创建新对象 return 0; }
在上述代码中,我们为Student类添加了一个拷贝构造函数。拷贝构造函数的作用是根据现有的Student对象(参数为const Student& 类型)创建一个新对象,并进行深拷贝,以防止多个对象共享同一块内存。
6. 如何设计类
在设计类时,我们需要考虑类的成员变量和成员函数的设计,以及类的访问权限等问题。
类的成员变量应该表示对象的状态,而成员函数则是对象的行为- 类的设计应遵循面向对象的原则,如封装、继承和多态,以及良好的代码风格。
【例1-10】完整的Rect类
以下是一个完整的Rect类的示例代码:
#include <iostream> class Rect { public: Rect() { width = 0; height = 0; } Rect(int w, int h) { width w; height = h; } int getArea() { return width * height; } // 访问器函数 int getWidth() const { return width; } int getHeight() const { return height; } // 修改器函数 void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } private: int width; int height; }; int main() { Rect r1(5, 7); std::cout << "r1的面积:" << r1.getArea() << std::endl; Rect r2; r2.setWidth(10); r2.setHeight(3); std::cout << "r2的面积:" << r2.getArea() << std::endl; return 0; }
在上述代码中,我们定义了一个名为Rect的类,表示矩形对象。类中包含了构造函数、访问器函数和修改器函数,用于创建对象获取对象的属性和修改的属性。
7. 综合实例
【例1-11】设计复数类
以下是一个设计复数类的示例代码:
#include <iostream> class Complex { public: Complex() { real = 0; imag = 0; } Complex(double r, double i) { real = r; imag = i; } // 加法运算符重载 Complex operator+(const Complex& other) { Complex result; result.real = real + other.real; result.imag = imag + other.imag; return result; } // 输出运算符重载 friend std::ostream& operator<<(std::ostream& os, const Complex& c) { os << c.real << "+" << c.imag << "i"; return os; } private: double real; double imag; }; int main() { Complex c1(1, 2); Complex c2(3, 4); Complex c3 = c1 + c2; std::cout << "c1 + c2 = " << c3 << std::endl; return 0; }
在上述代码中,我们定义了一个名为Complex的类,表示复数对象。类中包含了构造函数和运算符重载函数,用于创建复数并进行加法运算。通过运算符重载,我们可以使用自定义的方式对对象进行加法操作,并重载输出运算符,方便输出复数对象的值。
【例1-12】设计时间类
以下是一个设计时间类的示例代码:
#include <iostream> class Time { public: Time(int h, int m, int s) { hour = h; minute = m; second = s; } void display() { std::cout << "当前时间:" << hour << ":" << minute << ":" << second << std::endl; } // 加法运算符重载 Time operator+(const Time& other) { Time result(0, 0, 0); result.second = (second + other.second) % 60; result.minute = (minute + (second + other.second) / 60 + other.minute) % 60; result.hour = (hour + (minute + (second + other.second) / 60 + other.minute) / 60 + other.hour) % 24; return result; } private: int hour; int minute; int second; }; int main() { Time t1(10, 20, 30); Time t2(1, 40, 50); Time t3 = t1 + t2; std::cout << "t1 + t2 = "; t3.display(); return 0; }
在上述代码中,我们定义了一个名为Time的类,时间对象。类中包了构造函数、显示函数和运算符重载函数,用于创建时间对象、显示时间和进行时间的加法运算。通过运算符重载,我们可以使用自定义的方式对时间对象进行加法操作,并以规定格式输出时间。