文章目录
1. 概述
之前的一篇文章大致解释了面向对象编程的四大概念:
C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。
类用于指定对象的形式,是一种用户自定义的数据类型,它是一种封装了数据和函数的组合。类中的数据称为成员变量,函数称为成员函数。类可以被看作是一种模板,可以用来创建具有相同属性和行为的多个对象。
类定义
定义一个类需要使用关键字 class,然后指定类的名称,并类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数。
定义一个类,本质上是定义一个数据类型的蓝图,它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。
以下示例展示了如何定义一个类 Circle
,创建其对象,并访问其成员变量。
#include <iostream>
using namespace std;
class Circle {
public:
double radius; // 圆的半径
double x; // 圆心的 x 坐标
double y; // 圆心的 y 坐标
// 成员函数,用于计算圆的面积
double area() {
return 3.14159 * radius * radius;
}
};
int main() {
Circle circle1; // 声明一个 Circle 对象
Circle circle2; // 再次声明一个 Circle 对象
// 对第一个 circle 对象进行赋值,并计算面积
circle1.radius = 5.0;
circle1.x = 2.0;
circle1.y = 3.0;
cout << "Circle 1 Area: " << circle1.area() << endl;
// 对第二个 circle 对象进行赋值,并计算面积
circle2.radius = 10.0;
circle2.x = 1.0;
circle2.y = 1.0;
cout << "Circle 2 Area: " << circle2.area() << endl;
return 0;
}
-
类声明:
- 类
Circle
声明包含三个公共成员变量:radius
、x
和y
。 - 同时声明了一个成员函数
area()
用于计算圆的面积。
- 类
-
对象定义:
Circle circle1;
和Circle circle2;
定义了两个Circle
对象circle1
和circle2
。
-
成员赋值和访问:
- 为
circle1
和circle2
的成员赋值,如circle1.radius = 5.0;
。 - 调用成员函数计算圆的面积:
circle1.area()
和circle2.area()
。
- 为
输出结果
Circle 1 Area: 78.5398
Circle 2 Area: 314.159
访问数据成员
类的对象的公共数据成员可以使用直接成员访问运算符 . 来访问。
代码示例:
#include <iostream>
using namespace std;
class Product {
public:
int quantity; // 产品数量
double price; // 产品单价
// 成员函数声明
double getTotalValue(void);
void set(int qty, double prc);
};
// 成员函数定义
double Product::getTotalValue(void) {
return quantity * price;
}
void Product::set(int qty, double prc) {
quantity = qty;
price = prc;
}
int main() {
Product Product1; // 声明 Product1,类型为 Product
Product Product2; // 声明 Product2,类型为 Product
Product Product3; // 声明 Product3,类型为 Product
double totalValue = 0.0;
// Product1 详述
Product1.quantity = 50;
Product1.price = 12.5;
// Product2 详述
Product2.quantity = 100;
Product2.price = 20.0;
// Product1 的总价值
totalValue = Product1.quantity * Product1.price;
cout << "Product1 的总价值:" << totalValue << endl;
// Product2 的总价值
totalValue = Product2.quantity * Product2.price;
cout << "Product2 的总价值:" << totalValue << endl;
// Product3 详述
Product3.set(200, 15.0);
totalValue = Product3.getTotalValue();
cout << "Product3 的总价值:" << totalValue << endl;
return 0;
}
需要注意的是,私有的成员和受保护的成员不能使用直接成员访问运算符 (.) 来直接访问。
2. 类 & 对象相关概念
概念 | 描述 |
---|---|
类成员函数 | 类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。 |
类访问修饰符 | 类成员可以被定义为 public、private 或 protected。默认情况下是定义为 private。 |
构造函数 & 析构函数 | 类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。 |
C++ 拷贝构造函数 | 拷贝构造函数,是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。 |
C++ 友元函数 | 友元函数可以访问类的 private 和 protected 成员。 |
C++ 内联函数 | 通过内联函数,编译器试图在调用函数的地方扩展函数体中的代码。 |
C++ 中的 this 指针 | 每个对象都有一个特殊的指针 this,它指向对象本身。 |
C++ 中指向类的指针 | 指向类的指针方式如同指向结构的指针。实际上,类可以看成是一个带有函数的结构。 |
C++ 类的静态成员 | 类的数据成员和函数成员都可以被声明为静态的。 |
3. 类成员函数
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
看看之前定义的类 Car,现在要使用成员函数来访问类的成员,而不是直接访问这些类的成员:
class Car {
public:
double speed; // 速度
double fuel; // 燃料量
int passengers; // 乘客数量
double getRange(void); // 返回续航能力
};
成员函数可以定义在类定义的内部,或者单独使用范围解析运算符 ::
来定义。在类定义中定成员函数把函数声明为内联的,即便没有使用 inline 标识符。所以可以按照如下方式定义 getRange
函数:
class Car {
public:
double speed; // 速度
double fuel; // 燃料量
int passengers; // 乘客数量
double getRange(void) {
return fuel * 20; // 假设燃料效率为每单位燃料20公里
}
};
当然也可以在类的外部使用范围解析运算符 :: 定义该函数,如下所示:
double Car::getRange(void) {
return fuel * 20; // 假设燃料效率为每单位燃料20公里
}
在这里,需要强调一点,在 :: 运算符之前必须使用类名。调用成员函数是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据,如下所示:
Car myCar; // 创建一个对象
myCar.getRange(); // 调用该对象的成员函数
接下来使用上面提到的概念来设置和获取类中不同的成员的值:
代码示例:
#include <iostream>
using namespace std;
class Car {
public:
double speed; // 汽车速度
double fuel; // 汽车燃料量
int passengers; // 汽车乘客数
// 成员函数声明
double getRange(void);
void setSpeed(double spd);
void setFuel(double fl);
void setPassengers(int pass);
};
// 成员函数定义
double Car::getRange(void) {
// 假设燃料效率为每单位燃料20公里
return fuel * 20;
}
void Car::setSpeed(double spd) {
speed = spd;
}
void Car::setFuel(double fl) {
fuel = fl;
}
void Car::setPassengers(int pass) {
passengers = pass;
}
// 程序的主函数
int main() {
Car Car1; // 声明 Car1,类型为 Car
Car Car2; // 声明 Car2,类型为 Car
double range = 0.0; // 用于存储汽车续航能力
// Car1 详细信息
Car1.setSpeed(120.0);
Car1.setFuel(50.0);
Car1.setPassengers(4);
// Car2 详细信息
Car2.setSpeed(90.0);
Car2.setFuel(70.0);
Car2.setPassengers(2);
// Car1 的续航能力
range = Car1.getRange();
cout << "Car1 的续航能力:" << range << " 公里" << endl;
// Car2 的续航能力
range = Car2.getRange();
cout << "Car2 的续航能力:" << range << " 公里" << endl;
return 0;
}
Car1 的续航能力:1000 公里
Car2 的续航能力:1400 公里
4. 类访问修饰符
数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员。类成员的访问限制是通过在类主体内部对各个区域标记 public、private、protected 来指定的。关键字 public、private、protected 称为访问修饰符。
一个类可以有多个 public、protected 或 private 标记区域。每个标记区域在下一个标记区域开始之前或者在遇到类主体结束右括号之前都是有效的。成员和类的默认访问修饰符是 private。
class Base {
public:
// 公有成员
protected:
// 受保护成员
private:
// 私有成员
};
公有(public)成员
公有成员在程序中类的外部是可访问的。可以不使用任何成员函数来设置和获取公有变量的值,如下所示:
这一部分代码展示了如何通过成员函数来设置和获取类中的成员值,并且展示了如何直接访问公共成员变量。
#include <iostream>
using namespace std;
class Car {
public:
double speed; // 汽车速度
void setSpeed(double spd);
double getSpeed(void);
};
// 成员函数定义
double Car::getSpeed(void) {
return speed;
}
void Car::setSpeed(double spd) {
speed = spd;
}
// 程序的主函数
int main() {
Car myCar;
// 设置速度
myCar.setSpeed(120.0);
cout << "Current speed of the car: " << myCar.getSpeed() << " km/h" << endl;
// 不使用成员函数直接设置速度
myCar.speed = 150.0; // OK: 因为 speed 是公有的
cout << "Updated speed of the car: " << myCar.speed << " km/h" << endl;
return 0;
}
Current speed of the car: 120 km/h
Updated speed of the car: 150 km/h
私有(private)成员
私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
默认情况下,类的所有成员都是私有的。例如在下面的类中,fuel
是一个私有成员,这意味着,如果没有使用任何访问修饰符,类的成员将被假定为私有成员:
class Car {
double fuel; // 私有成员:燃料量
public:
double speed; // 公有成员:速度
void setFuel(double fuelAmount);
double getFuel(void);
};
实际操作中,我们一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数,如下所示:
#include <iostream>
using namespace std;
class Car {
public:
double speed; // 公有成员:速度
void setFuel(double fuelAmount);
double getFuel(void);
private:
double fuel; // 私有成员:燃料量
};
// 成员函数定义
double Car::getFuel(void) {
return fuel;
}
void Car::setFuel(double fuelAmount) {
fuel = fuelAmount;
}
// 程序的主函数
int main() {
Car car;
// 不使用成员函数设置速度
car.speed = 80.0; // OK: 因为 speed 是公有的
cout << "Speed of the car: " << car.speed << " km/h" << endl;
// 不能不使用成员函数设置燃料量
// car.fuel = 50.0; // Error: 因为 fuel 是私有的
car.setFuel(50.0); // 使用成员函数设置燃料量
cout << "Fuel of the car: " << car.getFuel() << " liters" << endl;
return 0;
}
Speed of the car: 80 km/h
Fuel of the car: 50 liters
protected(受保护)成员
protected(受保护)成员变量或函数与私有成员十分相似,但有一点不同,protected(受保护)成员在派生类(即子类)中是可访问的。
下面的实例中,我们从父类 Car
派生了一个子类 SportsCar
。
下面的实例与前面的实例类似,在这里 speed
成员可被派生类 SportsCar
的任何成员函数访问。
#include <iostream>
using namespace std;
class Car {
protected:
double speed;
};
class SportsCar : public Car { // SportsCar 是派生类
public:
void setMaxSpeed(double spd);
double getMaxSpeed(void);
};
// 子类的成员函数
double SportsCar::getMaxSpeed(void) {
return speed;
}
void SportsCar::setMaxSpeed(double spd) {
speed = spd;
}
// 程序的主函数
int main() {
SportsCar car;
// 使用成员函数设置速度
car.setMaxSpeed(220.0);
cout << "Max speed of the sports car: " << car.getMaxSpeed() << " km/h" << endl;
return 0;
}
Max speed of the sports car: 220 km/h
继承中的特点
有public, protected, private三种继承方式,它们相应地改变了基类成员的访问属性。
-
public 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
-
protected 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private
-
private 继承:基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private
但无论哪种继承方式,下面两点都没有改变:
-
private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
-
protected 成员可以被派生类访问。
public 继承
代码示例:
#include <iostream>
using namespace std;
class Car {
public:
Car() {
maxSpeed = 200;
fuelEfficiency = 15;
engineType = 1;
vin = 1234;
}
void displayCarInfo() {
cout << "Max Speed: " << maxSpeed << " km/h" << endl;
cout << "Fuel Efficiency: " << fuelEfficiency << " km/l" << endl;
cout << "Engine Type: " << engineType << endl;
cout << "VIN: " << vin << endl;
}
public:
int maxSpeed;
protected:
int fuelEfficiency;
private:
int vin;
int engineType;
};
class ElectricCar : public Car {
public:
ElectricCar(int range) {
batteryRange = range;
}
void displayElectricCarInfo() {
cout << "Battery Range: " << batteryRange << " km" << endl; // 正确,public成员
cout << "Max Speed: " << maxSpeed << " km/h" << endl; // 正确,基类的public成员,在派生类中仍是public成员。
cout << "Fuel Efficiency: " << fuelEfficiency << " km/l" << endl; // 正确,基类的protected成员,在派生类中仍是protected可以被派生类访问。
// cout << "VIN: " << vin << endl; // 错误,基类的private成员不能被派生类访问。
// cout << "Engine Type: " << engineType << endl; // 错误,基类的private成员不能被派生类访问。
}
private:
int batteryRange;
};
int main() {
ElectricCar ecar(350);
cout << "Electric Car Info:" << endl;
ecar.displayElectricCarInfo();
cout << "Accessing base class members directly:" << endl;
cout << "Max Speed: " << ecar.maxSpeed << " km/h" << endl; // 正确
// cout << "Fuel Efficiency: " << ecar.fuelEfficiency << " km/l" << endl; // 错误,类外不能访问protected成员
// cout << "VIN: " << ecar.vin << endl; // 错误,类外不能访问private成员
return 0;
}
-
类的定义:
- 类
Car
包含四个成员变量maxSpeed
(最大速度)、fuelEfficiency
(燃料效率)、vin
(车辆识别号码)和engineType
(发动机类型),分别为public
、protected
和private
成员。 - 类中还包含一个成员函数
displayCarInfo()
,用于显示汽车的信息。
- 类
-
派生类的定义:
- 类
ElectricCar
通过public Car
继承自基类Car
。它新增了一个batteryRange
(电池续航里程)成员。 - 成员函数
displayElectricCarInfo()
可以访问基类的public
成员和protected
成员,但无法访问private
成员。
- 类
-
对象定义和使用:
- 声明
ElectricCar
对象ecar
,并调用其成员函数displayElectricCarInfo()
以展示信息。 - 直接访问基类的
public
成员,并尝试访问(注释掉)protected
和private
成员以演示访问控制。
- 声明
Electric Car Info:
Battery Range: 350 km
Max Speed: 200 km/h
Fuel Efficiency: 15 km/lAccessing base class members directly:
Max Speed: 200 km/h
protected 继承
代码示例:
#include <iostream>
using namespace std;
class Car {
public:
int maxSpeed;
Car() {
speed = 150;
fuelEfficiency = 20;
vin = 1234;
maxSpeed = 180;
}
void displayCarInfo() {
cout << "Max Speed: " << maxSpeed << " km/h" << endl;
cout << "Speed: " << speed << " km/h" << endl;
cout << "Fuel Efficiency: " << fuelEfficiency << " km/l" << endl;
cout << "VIN: " << vin << endl;
}
public:
int speed;
protected:
int fuelEfficiency;
private:
int vin;
};
class ElectricCar : protected Car {
public:
int batteryRange;
ElectricCar(int range) {
Car();
batteryRange = range;
}
void displayElectricCarInfo() {
cout << "Battery Range: " << batteryRange << " km" << endl;
cout << "Speed: " << speed << " km/h" << endl; // 正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。
cout << "Fuel Efficiency: " << fuelEfficiency << " km/l" << endl; // 正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。
// cout << "VIN: " << vin << endl; // 错误,基类的private成员不能被派生类访问。
}
};
int main() {
ElectricCar eCar(300);
cout << "Electric Car Info:" << endl;
eCar.displayElectricCarInfo();
cout << "Accessing base class members directly:" << endl;
// cout << "Speed: " << eCar.speed << " km/h" << endl; // 错误,protected成员不能在类外访问。
// cout << "Fuel Efficiency: " << eCar.fuelEfficiency << " km/l" << endl; // 错误,protected成员不能在类外访问。
// cout << "VIN: " << eCar.vin << endl; // 错误,private成员不能在类外访问。
return 0;
}
-
类的定义:
- 类
Car
包含四个成员变量speed
(速度)、fuelEfficiency
(燃料效率)、vin
(车辆识别号码)和maxSpeed
(最大速度),分别为public
、protected
和private
成员。 - 类中还包含一个成员函数
displayCarInfo()
,用于显示汽车的信息。
- 类
-
派生类的定义:
- 类
ElectricCar
通过protected Car
继承自基类Car
。它新增了一个batteryRange
(电池续航里程)成员。 - 成员函数
displayElectricCarInfo()
可以访问基类的public
成员(变成了protected
)和protected
成员,但无法访问private
成员。
- 类
-
对象定义和使用:
- 声明
ElectricCar
对象eCar
,并调用其成员函数displayElectricCarInfo()
以展示信息。 - 直接访问基类的
protected
成员,并尝试访问(注释掉)private
成员以演示访问控制。
- 声明
Electric Car Info:
Battery Range: 300 km
Speed: 150 km/h
Fuel Efficiency: 20 km/lAccessing base class members directly:
private 继承
代码示例:
#include <iostream>
using namespace std;
class Car {
public:
Car() {
maxSpeed = 180;
fuelEfficiency = 15;
engineType = "V8";
vin = 123456;
}
void displayCarInfo() {
cout << "Max Speed: " << maxSpeed << " km/h" << endl;
cout << "Fuel Efficiency: " << fuelEfficiency << " km/l" << endl;
cout << "Engine Type: " << engineType << endl;
cout << "VIN: " << vin << endl;
}
public:
int maxSpeed;
protected:
int fuelEfficiency;
private:
int vin;
string engineType;
};
class ElectricCar : private Car {
public:
ElectricCar(int range) {
batteryRange = range;
}
void displayElectricCarInfo() {
cout << "Battery Range: " << batteryRange << " km" << endl;
cout << "Max Speed: " << maxSpeed << " km/h" << endl; // 正确,基类public成员,在派生类中变成了private,可以被派生类访问。
cout << "Fuel Efficiency: " << fuelEfficiency << " km/l" << endl; // 正确,基类的protected成员,在派生类中变成了private,可以被派生类访问。
// cout << "Engine Type: " << engineType << endl; // 错误,基类的private成员不能被派生类访问。
// cout << "VIN: " << vin << endl; // 错误,基类的private成员不能被派生类访问。
}
private:
int batteryRange;
};
int main() {
ElectricCar eCar(300);
cout << "Electric Car Info:" << endl;
eCar.displayElectricCarInfo();
cout << "Accessing base class members directly:" << endl;
cout << "Battery Range: " << eCar.batteryRange << " km" << endl; // 正确。public成员
// cout << "Max Speed: " << eCar.maxSpeed << " km/h" << endl; // 错误,private成员不能在类外访问。
// cout << "Fuel Efficiency: " << eCar.fuelEfficiency << " km/l" << endl; // 错误, private成员不能在类外访问。
// cout << "Engine Type: " << eCar.engineType << endl; // 错误,private成员不能在类外访问。
// cout << "VIN: " << eCar.vin << endl; // 错误, private成员不能在类外访问。
return 0;
}
Electric Car Info:
Battery Range: 300 km
Max Speed: 180 km/h
Fuel Efficiency: 15 km/lAccessing base class members directly:
Battery Range: 300 km
5. 类构造函数 & 析构函数
类的构造函数
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
代码示例:
#include <iostream>
using namespace std;
class Car {
public:
void setMaxSpeed(double speed);
double getMaxSpeed(void);
Car(); // 这是构造函数
private:
double maxSpeed;
};
// 成员函数定义,包括构造函数
Car::Car(void) {
cout << "Car object is being created" << endl;
}
void Car::setMaxSpeed(double speed) {
maxSpeed = speed;
}
double Car::getMaxSpeed(void) {
return maxSpeed;
}
// 程序的主函数
int main() {
Car car;
// 设置最大速度
car.setMaxSpeed(220.0);
cout << "Max speed of car: " << car.getMaxSpeed() << " km/h" << endl;
return 0;
}
Car object is being created
Max speed of car: 220 km/h
带参数的构造函数
默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:
#include <iostream>
using namespace std;
class Car {
public:
void setMaxSpeed(double speed);
double getMaxSpeed(void);
Car(double speed); // 这是带参数的构造函数
private:
double maxSpeed;
};
// 成员函数定义,包括带参数的构造函数
Car::Car(double speed) {
cout << "Car object is being created, initial max speed = " << speed << " km/h" << endl;
maxSpeed = speed;
}
void Car::setMaxSpeed(double speed) {
maxSpeed = speed;
}
double Car::getMaxSpeed(void) {
return maxSpeed;
}
// 程序的主函数
int main() {
Car car(220.0);
// 获取默认设置的最大速度
cout << "Max speed of car: " << car.getMaxSpeed() << " km/h" << endl;
// 再次设置最大速度
car.setMaxSpeed(180.0);
cout << "Max speed of car: " << car.getMaxSpeed() << " km/h" << endl;
return 0;
}
Car object is being created, initial max speed = 220 km/h
Max speed of car: 220 km/h
Max speed of car: 180 km/h
使用初始化列表来初始化字段
使用初始化列表来初始化字段:
Line::Line( double len): length(len)
{
cout << "Object is being created, length = " << len << endl;
}
上面的语法等同于如下语法:
Line::Line( double len)
{
length = len;
cout << "Object is being created, length = " << len << endl;
}
假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}
类的析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
代码示例:
#include <iostream>
using namespace std;
class Car {
public:
void setMaxSpeed(double speed);
double getMaxSpeed(void);
Car(); // 这是构造函数声明
~Car(); // 这是析构函数声明
private:
double maxSpeed;
};
// 成员函数定义,包括构造函数和析构函数
Car::Car(void) {
cout << "Car object is being created" << endl;
maxSpeed = 0.0;
}
Car::~Car(void) {
cout << "Car object is being deleted" << endl;
}
void Car::setMaxSpeed(double speed) {
maxSpeed = speed;
}
double Car::getMaxSpeed(void) {
return maxSpeed;
}
// 程序的主函数
int main() {
Car car;
// 设置最大速度
car.setMaxSpeed(220.0);
cout << "Max speed of car: " << car.getMaxSpeed() << " km/h" << endl;
return 0;
}
Car object is being created
Max speed of car: 220 km/h
Car object is being deleted