1315 lines
30 KiB
Markdown
1315 lines
30 KiB
Markdown
---
|
||
id: 面对对象
|
||
title: 面对对象
|
||
sidebar_position: 5
|
||
data: 2022年5月26日
|
||
---
|
||
|
||
C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。
|
||
|
||
类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成员。
|
||
|
||
## 类
|
||
|
||
类定义是以关键字 **class** 开头,后跟类的名称。类的主体是包含在一对花括号中。类定义后必须跟着一个分号或一个声明列表。
|
||
|
||
### 定义
|
||
|
||
```cpp
|
||
class Box
|
||
{
|
||
public:
|
||
double length; // 盒子的长度
|
||
double breadth; // 盒子的宽度
|
||
double height; // 盒子的高度
|
||
};
|
||
```
|
||
|
||
### 创建
|
||
|
||
对象 Box1 和 Box2 拥有独立的数据成员。
|
||
|
||
```cpp
|
||
Box Box1; // 声明 Box1,类型为 Box
|
||
Box Box2; // 声明 Box2,类型为 Box
|
||
```
|
||
|
||
### 实例
|
||
|
||
类的对象的公共数据成员可以使用直接成员访问运算符 (.) 来访问。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
double length; // 长度
|
||
double breadth; // 宽度
|
||
double height; // 高度
|
||
};
|
||
|
||
int main( )
|
||
{
|
||
Box Box1; // 声明 Box1,类型为 Box
|
||
Box Box2; // 声明 Box2,类型为 Box
|
||
double volume = 0.0; // 用于存储体积
|
||
|
||
// box 1 详述
|
||
Box1.height = 5.0;
|
||
Box1.length = 6.0;
|
||
Box1.breadth = 7.0;
|
||
|
||
// box 2 详述
|
||
Box2.height = 10.0;
|
||
Box2.length = 12.0;
|
||
Box2.breadth = 13.0;
|
||
|
||
// box 1 的体积
|
||
volume = Box1.height * Box1.length * Box1.breadth;
|
||
cout << "Box1 的体积:" << volume <<endl;
|
||
|
||
// box 2 的体积
|
||
volume = Box2.height * Box2.length * Box2.breadth;
|
||
cout << "Box2 的体积:" << volume <<endl;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Box1 的体积:210
|
||
Box2 的体积:1560
|
||
```
|
||
|
||
## 类的详解
|
||
|
||
### 构造函数
|
||
|
||
类的构造函数是类的一种特殊的成员函数,它会在**每次创建类的新对象时执行。**
|
||
|
||
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
|
||
|
||
如果需要,构造函数也可以带有参数,可以通过重载实现构造函数多元化。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Line
|
||
{
|
||
public:
|
||
void setLength( double len );
|
||
double getLength( void );
|
||
Line(); // 这是构造函数
|
||
|
||
private:
|
||
double length;
|
||
};
|
||
|
||
// 成员函数定义,包括构造函数
|
||
Line::Line(void)
|
||
{
|
||
cout << "Object is being created" << endl;
|
||
}
|
||
|
||
void Line::setLength( double len )
|
||
{
|
||
length = len;
|
||
}
|
||
|
||
double Line::getLength( void )
|
||
{
|
||
return length;
|
||
}
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
Line line;
|
||
|
||
// 设置长度
|
||
line.setLength(6.0);
|
||
cout << "Length of line : " << line.getLength() <<endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
#### 初始化列表
|
||
|
||
假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:
|
||
|
||
```cpp
|
||
C::C( double a, double b, double c): X(a), Y(b), Z(c)
|
||
{
|
||
....
|
||
}
|
||
```
|
||
|
||
**等同于:**
|
||
|
||
```
|
||
C::C( double a, double b, double c)
|
||
{
|
||
X = a;
|
||
Y = b;
|
||
Z = c;
|
||
}
|
||
```
|
||
|
||
#### 拷贝构造函数
|
||
|
||
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
|
||
|
||
- 通过使用另一个同类型的对象来初始化新创建的对象。
|
||
- 复制对象把它作为参数传递给函数。
|
||
- 复制对象,并从函数返回这个对象。
|
||
|
||
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:
|
||
|
||
```cpp
|
||
classname (const classname &obj) {
|
||
// 构造函数的主体
|
||
}
|
||
```
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Line
|
||
{
|
||
public:
|
||
int getLength( void );
|
||
Line( int len ); // 简单的构造函数
|
||
Line( const Line &obj); // 拷贝构造函数
|
||
~Line(); // 析构函数
|
||
|
||
private:
|
||
int *ptr;
|
||
};
|
||
|
||
// 成员函数定义,包括构造函数
|
||
Line::Line(int len)
|
||
{
|
||
cout << "调用构造函数" << endl;
|
||
// 为指针分配内存
|
||
ptr = new int;
|
||
*ptr = len;
|
||
}
|
||
|
||
Line::Line(const Line &obj)
|
||
{
|
||
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
|
||
ptr = new int;
|
||
*ptr = *obj.ptr; // 拷贝值
|
||
}
|
||
|
||
Line::~Line(void)
|
||
{
|
||
cout << "释放内存" << endl;
|
||
delete ptr;
|
||
}
|
||
int Line::getLength( void )
|
||
{
|
||
return *ptr;
|
||
}
|
||
|
||
void display(Line obj)
|
||
{
|
||
cout << "line 大小 : " << obj.getLength() <<endl;
|
||
}
|
||
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
Line line(10);
|
||
|
||
display(line);
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
调用构造函数
|
||
调用拷贝构造函数并为指针 ptr 分配内存
|
||
line 大小 : 10
|
||
释放内存
|
||
释放内存
|
||
```
|
||
|
||
### 析构函数
|
||
|
||
类的**析构函数**是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
|
||
|
||
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
|
||
|
||
#### 实例
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Line
|
||
{
|
||
public:
|
||
void setLength( double len );
|
||
double getLength( void );
|
||
Line(); // 这是构造函数声明
|
||
~Line(); // 这是析构函数声明
|
||
|
||
private:
|
||
double length;
|
||
};
|
||
|
||
// 成员函数定义,包括构造函数
|
||
Line::Line(void)
|
||
{
|
||
cout << "Object is being created" << endl;
|
||
}
|
||
Line::~Line(void)
|
||
{
|
||
cout << "Object is being deleted" << endl;
|
||
}
|
||
|
||
void Line::setLength( double len )
|
||
{
|
||
length = len;
|
||
}
|
||
|
||
double Line::getLength( void )
|
||
{
|
||
return length;
|
||
}
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
Line line;
|
||
|
||
// 设置长度
|
||
line.setLength(6.0);
|
||
cout << "Length of line : " << line.getLength() <<endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Object is being created
|
||
Length of line : 6
|
||
Object is being deleted
|
||
```
|
||
|
||
### 访问修饰符
|
||
|
||
数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员。类成员的访问限制是通过在类主体内部对各个区域标记 **public、private、protected** 来指定的。关键字 **public、private、protected** 称为访问修饰符。
|
||
|
||
```cpp
|
||
class Base {
|
||
|
||
public:
|
||
|
||
// 公有成员
|
||
|
||
protected:
|
||
|
||
// 受保护成员
|
||
|
||
private:
|
||
|
||
// 私有成员
|
||
|
||
};
|
||
```
|
||
|
||
- **公有(public)成员**:公有成员在程序中类的外部是**可访问的**。
|
||
- **私有(private)成员**:私有成员变量或函数在类的外部是**不可访问、查看的**。只有类和友元函数可以访问私有成员。**默认情况下,类的所有成员都是私有的。**
|
||
- **保护(protected)成员**:保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。
|
||
|
||
-
|
||
|
||
### 成员函数
|
||
|
||
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
|
||
|
||
```cpp
|
||
class Box
|
||
{
|
||
public:
|
||
double length; // 长度
|
||
double breadth; // 宽度
|
||
double height; // 高度
|
||
|
||
double getVolume(void)
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
};
|
||
```
|
||
|
||
在类的内部声明函数后,可以在类的外部使用**范围解析运算符 ::** 定义该函数,在 **::** 运算符之前必须使用类名。
|
||
|
||
```cpp
|
||
class Box
|
||
{
|
||
public:
|
||
double length; // 长度
|
||
double breadth; // 宽度
|
||
double height; // 高度
|
||
|
||
double getVolume(void);// 必须在类的内部声明函数
|
||
};
|
||
|
||
double Box::getVolume(void)
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
```
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
double length; // 长度
|
||
double breadth; // 宽度
|
||
double height; // 高度
|
||
|
||
double getVolume(void)
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
|
||
// 成员函数声明
|
||
void setLength( double len );
|
||
void setBreadth( double bre );
|
||
void setHeight( double hei );
|
||
};
|
||
|
||
// 成员函数定义
|
||
void Box::setLength( double len )
|
||
{
|
||
length = len;
|
||
}
|
||
|
||
void Box::setBreadth( double bre )
|
||
{
|
||
breadth = bre;
|
||
}
|
||
|
||
void Box::setHeight( double hei )
|
||
{
|
||
height = hei;
|
||
}
|
||
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
Box Box1; // 声明 Box1,类型为 Box
|
||
Box Box2; // 声明 Box2,类型为 Box
|
||
double volume = 0.0; // 用于存储体积
|
||
|
||
// box 1 详述
|
||
Box1.setLength(6.0);
|
||
Box1.setBreadth(7.0);
|
||
Box1.setHeight(5.0);
|
||
|
||
// box 2 详述
|
||
Box2.setLength(12.0);
|
||
Box2.setBreadth(13.0);
|
||
Box2.setHeight(10.0);
|
||
|
||
// box 1 的体积
|
||
volume = Box1.getVolume();
|
||
cout << "Box1 的体积:" << volume <<endl;
|
||
|
||
// box 2 的体积
|
||
volume = Box2.getVolume();
|
||
cout << "Box2 的体积:" << volume <<endl;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```cpp
|
||
Box1 的体积: 210
|
||
Box2 的体积: 1560
|
||
```
|
||
|
||
### 友元函数
|
||
|
||
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是**友元函数并不是成员函数。**
|
||
|
||
**友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类**,在这种情况下,整个类及其所有成员都是友元。
|
||
|
||
```cpp
|
||
class Box
|
||
{
|
||
double width;
|
||
public:
|
||
double length;
|
||
friend void printWidth( Box box ); // 友元函数
|
||
friend class ClassTwo; // 友元类
|
||
void setWidth( double wid );
|
||
};
|
||
```
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
double width;
|
||
public:
|
||
friend void printWidth( Box box );
|
||
void setWidth( double wid );
|
||
};
|
||
|
||
// 成员函数定义
|
||
void Box::setWidth( double wid )
|
||
{
|
||
width = wid;
|
||
}
|
||
|
||
// 请注意:printWidth() 不是任何类的成员函数
|
||
void printWidth( Box box )
|
||
{
|
||
/* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
|
||
cout << "Width of box : " << box.width <<endl;
|
||
}
|
||
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
Box box;
|
||
|
||
// 使用成员函数设置宽度
|
||
box.setWidth(10.0);
|
||
|
||
// 使用友元函数输出宽度
|
||
printWidth( box );
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:Width of box : 10**
|
||
|
||
### 内联函数
|
||
|
||
C++ **内联函数**是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
|
||
|
||
内联的主要不同是:它会复制源函数代码到调用的位置,而不像普通函数一样进行现场保护、跳转执行然后恢复现场等操作,可以使程序效率提高,比较典型的空间换取时间的思路。
|
||
|
||
如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 **inline**,在调用函数之前需要对函数进行定义。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
inline int Max(int x, int y)
|
||
{
|
||
return (x > y)? x : y;
|
||
}
|
||
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
|
||
cout << "Max (20,10): " << Max(20,10) << endl;
|
||
cout << "Max (0,200): " << Max(0,200) << endl;
|
||
cout << "Max (100,1010): " << Max(100,1010) << endl;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```cpp
|
||
Max (20,10): 20
|
||
Max (0,200): 200
|
||
Max (100,1010): 1010
|
||
```
|
||
|
||
### 静态成员
|
||
|
||
可以使用 **static** 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。
|
||
|
||
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 **::** 来重新声明静态变量从而对它进行初始化,如下面的实例所示。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
static int objectCount;
|
||
// 构造函数定义
|
||
Box(double l=2.0, double b=2.0, double h=2.0)
|
||
{
|
||
cout <<"Constructor called." << endl;
|
||
length = l;
|
||
breadth = b;
|
||
height = h;
|
||
// 每次创建对象时增加 1
|
||
objectCount++;
|
||
}
|
||
double Volume()
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
private:
|
||
double length; // 长度
|
||
double breadth; // 宽度
|
||
double height; // 高度
|
||
};
|
||
|
||
// 初始化类 Box 的静态成员
|
||
int Box::objectCount = 0;
|
||
|
||
int main(void)
|
||
{
|
||
Box Box1(3.3, 1.2, 1.5); // 声明 box1
|
||
Box Box2(8.5, 6.0, 2.0); // 声明 box2
|
||
|
||
// 输出对象的总数
|
||
cout << "Total objects: " << Box::objectCount << endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Constructor called.
|
||
Constructor called.
|
||
Total objects: 2
|
||
```
|
||
|
||
### 静态成员函数
|
||
|
||
如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,**静态函数**只要使用类名加范围解析运算符 **::** 就可以访问。
|
||
|
||
静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
|
||
|
||
静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。
|
||
|
||
**静态成员函数与普通成员函数的区别:**
|
||
|
||
- 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
|
||
|
||
- 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
static int objectCount;
|
||
// 构造函数定义
|
||
Box(double l=2.0, double b=2.0, double h=2.0)
|
||
{
|
||
cout <<"Constructor called." << endl;
|
||
length = l;
|
||
breadth = b;
|
||
height = h;
|
||
// 每次创建对象时增加 1
|
||
objectCount++;
|
||
}
|
||
double Volume()
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
static int getCount()
|
||
{
|
||
return objectCount;
|
||
}
|
||
private:
|
||
double length; // 长度
|
||
double breadth; // 宽度
|
||
double height; // 高度
|
||
};
|
||
|
||
// 初始化类 Box 的静态成员
|
||
int Box::objectCount = 0;
|
||
|
||
int main(void)
|
||
{
|
||
|
||
// 在创建对象之前输出对象的总数
|
||
cout << "Inital Stage Count: " << Box::getCount() << endl;
|
||
|
||
Box Box1(3.3, 1.2, 1.5); // 声明 box1
|
||
Box Box2(8.5, 6.0, 2.0); // 声明 box2
|
||
|
||
// 在创建对象之后输出对象的总数
|
||
cout << "Final Stage Count: " << Box::getCount() << endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Inital Stage Count: 0
|
||
Constructor called.
|
||
Constructor called.
|
||
Final Stage Count: 2
|
||
```
|
||
|
||
### this 指针
|
||
|
||
在 C++ 中,每一个对象都能通过 **this** 指针来访问自己的地址。**this** 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
|
||
|
||
友元函数没有 **this** 指针,因为友元不是类的成员。只有成员函数才有 **this** 指针。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
// 构造函数定义
|
||
Box(double l=2.0, double b=2.0, double h=2.0)
|
||
{
|
||
cout <<"Constructor called." << endl;
|
||
length = l;
|
||
breadth = b;
|
||
height = h;
|
||
}
|
||
double Volume()
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
int compare(Box box)
|
||
{
|
||
return this->Volume() > box.Volume();
|
||
}
|
||
private:
|
||
double length; // Length of a box
|
||
double breadth; // Breadth of a box
|
||
double height; // Height of a box
|
||
};
|
||
|
||
int main(void)
|
||
{
|
||
Box Box1(3.3, 1.2, 1.5); // Declare box1
|
||
Box Box2(8.5, 6.0, 2.0); // Declare box2
|
||
|
||
if(Box1.compare(Box2))
|
||
{
|
||
cout << "Box2 is smaller than Box1" <<endl;
|
||
}
|
||
else
|
||
{
|
||
cout << "Box2 is equal to or larger than Box1" <<endl;
|
||
}
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Constructor called.
|
||
Constructor called.
|
||
Box2 is equal to or larger than Box1
|
||
```
|
||
|
||
### 指向类的指针
|
||
|
||
一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 **->**,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
// 构造函数定义
|
||
Box(double l=2.0, double b=2.0, double h=2.0)
|
||
{
|
||
cout <<"Constructor called." << endl;
|
||
length = l;
|
||
breadth = b;
|
||
height = h;
|
||
}
|
||
double Volume()
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
private:
|
||
double length; // Length of a box
|
||
double breadth; // Breadth of a box
|
||
double height; // Height of a box
|
||
};
|
||
|
||
int main(void)
|
||
{
|
||
Box Box1(3.3, 1.2, 1.5); // Declare box1
|
||
Box Box2(8.5, 6.0, 2.0); // Declare box2
|
||
Box *ptrBox; // Declare pointer to a class.
|
||
|
||
// 保存第一个对象的地址
|
||
ptrBox = &Box1;
|
||
|
||
// 现在尝试使用成员访问运算符来访问成员
|
||
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
|
||
|
||
// 保存第二个对象的地址
|
||
ptrBox = &Box2;
|
||
|
||
// 现在尝试使用成员访问运算符来访问成员
|
||
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Constructor called.
|
||
Constructor called.
|
||
Volume of Box1: 5.94
|
||
Volume of Box2: 102
|
||
```
|
||
|
||
一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 **->**,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
// 构造函数定义
|
||
Box(double l=2.0, double b=2.0, double h=2.0)
|
||
{
|
||
cout <<"Constructor called." << endl;
|
||
length = l;
|
||
breadth = b;
|
||
height = h;
|
||
}
|
||
double Volume()
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
private:
|
||
double length; // Length of a box
|
||
double breadth; // Breadth of a box
|
||
double height; // Height of a box
|
||
};
|
||
|
||
int main(void)
|
||
{
|
||
Box Box1(3.3, 1.2, 1.5); // Declare box1
|
||
Box Box2(8.5, 6.0, 2.0); // Declare box2
|
||
Box *ptrBox; // Declare pointer to a class.
|
||
|
||
// 保存第一个对象的地址
|
||
ptrBox = &Box1;
|
||
|
||
// 现在尝试使用成员访问运算符来访问成员
|
||
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
|
||
|
||
// 保存第二个对象的地址
|
||
ptrBox = &Box2;
|
||
|
||
// 现在尝试使用成员访问运算符来访问成员
|
||
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Constructor called.
|
||
Constructor called.
|
||
Volume of Box1: 5.94
|
||
Volume of Box2: 102
|
||
```
|
||
|
||
## 封装
|
||
|
||
封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装引申出了另一个重要的 OOP 概念,即数据隐藏。
|
||
|
||
数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
class Adder{
|
||
public:
|
||
// 构造函数
|
||
Adder(int i = 0)
|
||
{
|
||
total = i;
|
||
}
|
||
// 对外的接口
|
||
void addNum(int number)
|
||
{
|
||
total += number;
|
||
}
|
||
// 对外的接口
|
||
int getTotal()
|
||
{
|
||
return total;
|
||
};
|
||
private:
|
||
// 对外隐藏的数据
|
||
int total;
|
||
};
|
||
int main( )
|
||
{
|
||
Adder a;
|
||
|
||
a.addNum(10);
|
||
a.addNum(20);
|
||
a.addNum(30);
|
||
|
||
cout << "Total " << a.getTotal() <<endl;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:Total 60**
|
||
|
||
## 继承
|
||
|
||
继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。借此达到了重用代码功能和提高执行时间的效果。继承语法定义如下:
|
||
|
||
```cpp
|
||
// 单继承
|
||
class <派生类名>: <继承方式> <基类名>
|
||
|
||
// 多继承
|
||
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
|
||
{
|
||
<派生类类体>
|
||
};
|
||
```
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
using namespace std;
|
||
|
||
// 基类 Shape
|
||
class Shape
|
||
{
|
||
public:
|
||
void setWidth(int w)
|
||
{
|
||
width = w;
|
||
}
|
||
void setHeight(int h)
|
||
{
|
||
height = h;
|
||
}
|
||
protected:
|
||
int width;
|
||
int height;
|
||
};
|
||
|
||
// 基类 PaintCost
|
||
class PaintCost
|
||
{
|
||
public:
|
||
int getCost(int area)
|
||
{
|
||
return area * 70;
|
||
}
|
||
};
|
||
|
||
// 派生类
|
||
class Rectangle: public Shape, public PaintCost
|
||
{
|
||
public:
|
||
int getArea()
|
||
{
|
||
return (width * height);
|
||
}
|
||
};
|
||
|
||
int main(void)
|
||
{
|
||
Rectangle Rect;
|
||
int area;
|
||
|
||
Rect.setWidth(5);
|
||
Rect.setHeight(7);
|
||
|
||
area = Rect.getArea();
|
||
|
||
// 输出对象的面积
|
||
cout << "Total area: " << Rect.getArea() << endl;
|
||
|
||
// 输出总花费
|
||
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Total area: 35
|
||
Total paint cost: $2450
|
||
```
|
||
|
||
### 继承类型
|
||
|
||
有 public, protected, private 三种继承方式,它们相应地改变了基类成员的访问属性。
|
||
|
||
**public 继承:**
|
||
|
||
| 基类 | public | protected | private |
|
||
| ------ | ------ | --------- | ------- |
|
||
| 派生类 | 可 | 可 | 不可 |
|
||
| 类外 | 可 | 不可 | 不可 |
|
||
|
||
**protected 继承:**
|
||
|
||
| 基类 | public | protected | private |
|
||
| ------ | ------ | --------- | ------- |
|
||
| 派生类 | 可 | 可 | 不可 |
|
||
| 类外 | 不可 | 不可 | 不可 |
|
||
|
||
**private 继承:**
|
||
|
||
| 基类 | public | protected | private |
|
||
| ------ | ------ | --------- | ------- |
|
||
| 派生类 | 可 | 可 | 不可 |
|
||
| 类外 | 不可 | 不可 | 不可 |
|
||
|
||
无论哪种继承方式,有两点都没有改变:
|
||
|
||
- private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
|
||
- protected 成员可以被派生类访问。
|
||
|
||
## 多态
|
||
|
||
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
|
||
|
||
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
class Shape {
|
||
protected:
|
||
int width, height;
|
||
public:
|
||
Shape( int a=0, int b=0)
|
||
{
|
||
width = a;
|
||
height = b;
|
||
}
|
||
int area()
|
||
{
|
||
cout << "Parent class area :" <<endl;
|
||
return 0;
|
||
}
|
||
};
|
||
class Rectangle: public Shape{
|
||
public:
|
||
Rectangle( int a=0, int b=0):Shape(a, b) { }
|
||
int area ()
|
||
{
|
||
cout << "Rectangle class area :" <<endl;
|
||
return (width * height);
|
||
}
|
||
};
|
||
class Triangle: public Shape{
|
||
public:
|
||
Triangle( int a=0, int b=0):Shape(a, b) { }
|
||
int area ()
|
||
{
|
||
cout << "Triangle class area :" <<endl;
|
||
return (width * height / 2);
|
||
}
|
||
};
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
Shape *shape;
|
||
Rectangle rec(10,7);
|
||
Triangle tri(10,5);
|
||
|
||
// 存储矩形的地址
|
||
shape = &rec;
|
||
// 调用矩形的求面积函数 area
|
||
shape->area();
|
||
|
||
// 存储三角形的地址
|
||
shape = &tri;
|
||
// 调用三角形的求面积函数 area
|
||
shape->area();
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Parent class area
|
||
Parent class area
|
||
```
|
||
|
||
> 导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的**静态多态**,或**静态链接** - 函数调用在程序执行前就准备好了。有时候这也被称为**早绑定**,因为 area() 函数在程序编译期间就已经设置好了。
|
||
|
||
现在在 Shape 类中,area() 的声明前放置关键字 **virtual**,如下所示:
|
||
|
||
```cpp
|
||
class Shape {
|
||
protected:
|
||
int width, height;
|
||
public:
|
||
Shape( int a=0, int b=0)
|
||
{
|
||
width = a;
|
||
height = b;
|
||
}
|
||
virtual int area()
|
||
{
|
||
cout << "Parent class area :" <<endl;
|
||
return 0;
|
||
}
|
||
};
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Rectangle class area
|
||
Triangle class area
|
||
```
|
||
|
||
### 虚函数
|
||
|
||
虚函数是在基类中使用关键字 **virtual** 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
|
||
|
||
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为**动态链接**,或**后期绑定**。
|
||
|
||
**纯虚函数**
|
||
|
||
您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
|
||
|
||
```cpp
|
||
class Shape {
|
||
protected:
|
||
int width, height;
|
||
public:
|
||
Shape( int a=0, int b=0)
|
||
{
|
||
width = a;
|
||
height = b;
|
||
}
|
||
// 纯虚函数,= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
|
||
virtual int area() = 0;
|
||
};
|
||
```
|
||
|
||
### 重载函数
|
||
|
||
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
class printData
|
||
{
|
||
public:
|
||
void print(int i) {
|
||
cout << "整数为: " << i << endl;
|
||
}
|
||
|
||
void print(double f) {
|
||
cout << "浮点数为: " << f << endl;
|
||
}
|
||
|
||
void print(char c[]) {
|
||
cout << "字符串为: " << c << endl;
|
||
}
|
||
};
|
||
|
||
int main(void)
|
||
{
|
||
printData pd;
|
||
|
||
// 输出整数
|
||
pd.print(5);
|
||
// 输出浮点数
|
||
pd.print(500.263);
|
||
// 输出字符串
|
||
char c[] = "Hello C++";
|
||
pd.print(c);
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
整数为: 5
|
||
浮点数为: 500.263
|
||
字符串为: Hello C++
|
||
```
|
||
|
||
### 重载运算符
|
||
|
||
您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。
|
||
|
||
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
|
||
|
||
```cpp
|
||
Box operator+(const Box&);
|
||
```
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
class Box
|
||
{
|
||
public:
|
||
|
||
double getVolume(void)
|
||
{
|
||
return length * breadth * height;
|
||
}
|
||
void setLength( double len )
|
||
{
|
||
length = len;
|
||
}
|
||
|
||
void setBreadth( double bre )
|
||
{
|
||
breadth = bre;
|
||
}
|
||
|
||
void setHeight( double hei )
|
||
{
|
||
height = hei;
|
||
}
|
||
// 重载 + 运算符,用于把两个 Box 对象相加
|
||
Box operator+(const Box& b)
|
||
{
|
||
Box box;
|
||
box.length = this->length + b.length;
|
||
box.breadth = this->breadth + b.breadth;
|
||
box.height = this->height + b.height;
|
||
return box;
|
||
}
|
||
private:
|
||
double length; // 长度
|
||
double breadth; // 宽度
|
||
double height; // 高度
|
||
};
|
||
// 程序的主函数
|
||
int main( )
|
||
{
|
||
Box Box1; // 声明 Box1,类型为 Box
|
||
Box Box2; // 声明 Box2,类型为 Box
|
||
Box Box3; // 声明 Box3,类型为 Box
|
||
double volume = 0.0; // 把体积存储在该变量中
|
||
|
||
// Box1 详述
|
||
Box1.setLength(6.0);
|
||
Box1.setBreadth(7.0);
|
||
Box1.setHeight(5.0);
|
||
|
||
// Box2 详述
|
||
Box2.setLength(12.0);
|
||
Box2.setBreadth(13.0);
|
||
Box2.setHeight(10.0);
|
||
|
||
// Box1 的体积
|
||
volume = Box1.getVolume();
|
||
cout << "Volume of Box1 : " << volume <<endl;
|
||
|
||
// Box2 的体积
|
||
volume = Box2.getVolume();
|
||
cout << "Volume of Box2 : " << volume <<endl;
|
||
|
||
// 把两个对象相加,得到 Box3
|
||
Box3 = Box1 + Box2;
|
||
|
||
// Box3 的体积
|
||
volume = Box3.getVolume();
|
||
cout << "Volume of Box3 : " << volume <<endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**输出:**
|
||
|
||
```
|
||
Volume of Box1 : 210
|
||
Volume of Box2 : 1560
|
||
Volume of Box3 : 5400
|
||
```
|
||
|
||
#### 可重载运算符
|
||
|
||
| 双目算术运算符 | + (加),-(减),*(乘),/(除),% (取模) |
|
||
| :------------: | :----------------------------------------------------------: |
|
||
| 关系运算符 | ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于) |
|
||
| 逻辑运算符 | \|\|(逻辑或),&&(逻辑与),!(逻辑非) |
|
||
| 单目运算符 | + (正),-(负),*(指针),&(取地址) |
|
||
| 自增自减运算符 | ++(自增),--(自减) |
|
||
| 位运算符 | \| (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) |
|
||
| 赋值运算符 | =, +=, -=, *=, /= , % = , &=, \|=, ^=, <<=, >>= |
|
||
| 空间申请与释放 | new, delete, new[ ] , delete[] |
|
||
| 其他运算符 | **()**(函数调用),**->**(成员访问),**,**(逗号),**[]**(下标) |
|
||
|
||
#### 不可重载运算符
|
||
|
||
- **.**:成员访问运算符
|
||
- **.\***, **->\***:成员指针访问运算符
|
||
- **::**:域运算符
|
||
- **sizeof**:长度运算符
|
||
- **?:**:条件运算符
|
||
- **#**: 预处理符号
|