参考自菜鸟教程
本笔记适合复习重点,新手请参考其他教程!
数据抽象只向外界提供接口,并隐藏其后台的实现细节。
在 C++ 中,我们可以使用类来定义抽象数据类型(ADT)。
数据封装是一种把数据和操作数据的函数捆绑在一起的机制。
在 C++ 中,我们可以使用类来
类和对象
- 类是对对象的抽象,定义了对象的属性和行为。
- 对象是类的具体实例,是类的一个具体实体。
- 成员函数和成员对象是类的,而不是对象的。
- 在类的定义中,成员变量和成员函数如果没有显式指定访问权限,将被默认为私有成员。
- 示例代码:
class Box
{
public:
double length; // 盒子的长度
double breadth; // 盒子的宽度
double height; // 盒子的高度
};
- 定义 C++ 对象:
//使用构造函数定义对象,在栈上分配内存。
//它的生命周期通常与所在作用域的生命周期相对应:
Box Box1;
//使用new运算符在堆上动态分配内存,调用相应的构造函数来初始化对象,并返回指向分配内存的指针。
//使用delete释放内存,否则直到程序结束才会释放内存(造成内存泄漏)。
Box* Box2 = new Box()
- 访问数据成员:
Box1.height = 5.0;
构造函数和析构函数
构造函数和析构函数是特殊的成员函数
class Line
{
public:
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明
};
假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}
构造函数的执行流程如下:
-
分配内存: 首先,系统会为对象分配内存空间,这涉及到对象所需的存储空间的分配。
-
调用构造函数: 接着,调用对象的构造函数进行初始化。构造函数是类的成员函数,它的任务是确保对象在创建后处于一个合法和可用的状态。
-
初始化对象: 在构造函数中,对对象的成员变量进行初始化,分配资源,或执行其他必要的操作。
派生类构造函数的调用顺序如下: -
基类构造函数的调用: 派生类的构造函数首先调用其直接基类的构造函数。如果有多层继承,构造函数调用的顺序是从最顶层的基类开始,逐级向下调用。
-
成员对象的构造: 派生类构造函数接着调用其成员对象的构造函数,按照它们在类中声明的顺序调用。
-
派生类自身构造: 最后,派生类的构造函数执行自身的初始化。
当构造函数是private:
那么类的对象只能通过类的静态成员函数创建。
(类的静态成员函数不同实例对象就可以调用)。
class Singleton {
private:
// 私有构造函数,防止外部实例化
Singleton() {
// 构造函数的实现
}
public:
// 静态成员函数用于获取实例
static Singleton& getInstance() {
static Singleton instance; // 在第一次调用时创建实例
return instance;
}
};
类的静态成员和静态函数
我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。
我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化。
静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
继承
- 新建的类可以继承已有的类。已有的类称为基类,新建的类称为派生类。
- 派生类可以继承一个或多个基类
- 一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
示例:
// 基类
class Animal {
// eat() 函数
// sleep() 函数
};
class Friend {
// eat() 函数
// sleep() 函数
};
//派生类
class Dog : public Animal,Friend {
// bark() 函数
};
继承类型
- 当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。
- 通常使用 public 继承。
访问权限
派生类可以访问基类中所有的非私有成员。
重载
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
在类内定义重载(作为成员函数)是比较常见的做法。也可以在类外定义重载(重载函数作为普通函数/成员函数,重载运算符作为友元函数)。
重载函数
示例:重载print函数
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
}
重载运算符
和重载函数不同的地方在于函数名就是原名,而重载运算符的函数名是operator运算符。
示例:
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;
}
多态
使用visual修饰基类的成员函数。可以通过基类指针调用派生类成员函数
这个还是看原文比较好 https://www.runoob.com/cplusplus/cpp-polymorphism.html
静态链接根据数据类型确定函数,编译器在编译过程中将函数调用直接链接到被调用的函数的地址。
动态链接根据数据内容确定函数, 可执行文件中只包含对外部函数的引用,而实际的函数调用解析是在运行时由操作系统的动态链接器(dynamic linker)完成的。
虚函数
使用visual修饰的函数
纯虚函数
虚函数没有具体的实现:
virtual double getVolume() = 0;
接口/抽象类
- 如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。
- 抽象类不能被用于实例化对象。
- 可用于实例化对象的类被称为具体类。
定义抽象类class Box:
class Box
{
public:
// 纯虚函数
virtual double getVolume() = 0;
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
定义一个函数为虚函数,不代表函数为不被实现的函数。
虚函数是为了允许用基类的指针来调用子类的这个函数。
定义一个函数为纯虚函数,才代表函数没有被实现。
纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。