提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
C++是一种功能强大、灵活的编程语言,广泛应用于系统软件、应用软件、设备驱动、嵌入式软件、高性能服务器和客户端应用程序、游戏引擎等。
一、C和C++的区别
C 和 C++ 是两种广泛使用的编程语言,它们之间有许多区别。C++ 是在 C 的基础上发展而来的,增加了许多新特性和功能。以下是 C 和 C++ 之间的一些主要区别:
- 面向对象编程
C:是面向过程的编程语言,主要通过函数和过程进行编程。
C++:是面向对象的编程语言,支持类和对象。C++ 中的面向对象特性包括封装、继承和多态性。 - 数据抽象和封装
C:没有直接支持数据抽象和封装的语言特性,程序员需要通过结构体和函数进行模拟。
C++:引入了类和对象,通过访问控制符(public、private、protected)实现数据抽象和封装。 - 继承
C:不支持继承,无法通过一个类继承另一个类的属性和方法。
C++:支持继承,可以通过基类和派生类的关系重用代码。 - 多态性
C:不支持多态性,函数重载和运算符重载也不支持。
C++:支持多态性,包括函数重载和运算符重载,以及通过虚函数实现的动态多态性。 - 标准库
C:C 标准库提供了有限的函数,如 stdio.h、stdlib.h 等。
C++:C++ 标准库不仅包含 C 标准库的所有功能,还增加了如 STL(标准模板库),包括容器、迭代器和算法等。 - 内存管理
C:内存管理主要通过 malloc、calloc 和 free 等函数进行。
C++:内存管理可以通过 new 和 delete 操作符进行,此外还支持智能指针(如 std::unique_ptr 和 std::shared_ptr)来自动管理内存。 - 输入输出
C:使用标准的输入输出库函数,如 printf 和 scanf。
C++:使用更高级的 I/O 流库,如 cin 和 cout,支持类型安全和操作符重载。 - 函数和参数
C:函数参数不能有默认值。
C++:函数参数可以有默认值,支持更灵活的函数调用。 - 命名空间
C:没有命名空间,所有标识符共享一个全局命名空间,容易发生命名冲突。
C++:引入了命名空间,通过 namespace 关键字定义命名空间,避免命名冲突。 - 模板
C:不支持模板。
C++:支持模板,可以编写泛型代码,进行代码复用,如模板函数和模板类。 - 异常处理
C:不支持异常处理,错误处理通常通过返回值和错误代码进行。
C++:支持异常处理,通过 try、catch 和 throw 关键字处理异常。 - 兼容性
C:是 C++ 的子集,大部分 C 代码可以在 C++ 编译器下编译,但并非所有 C++ 代码都能在 C 编译器下编译。
C++:可以包含 C 代码并进行编译,但需要注意一些语法和特性的区别。
二、C++的灵魂——类
类(Class)可以被称为这门语言的“灵魂”,因为它不仅是面向对象编程(OOP)的核心,也是实现代码重用、模块化和抽象的关键。我们可以把类看作是c语言中结构体的升级版,它可以既可以包含变量,也可以包含函数(我们再leecode刷的题就是在进行类的编写)。
这里可以打个比方说明一下什么是类,比如有一条小狗,小狗有名字叫旺财,旺财的年龄是 2 岁,同时旺财会汪汪的叫,也能跑。我们统称狗这个为类,类是我们抽象出来的,因为狗不只有上面的属性,还有体重,毛发的颜色等等,我们只抽象出几种属性成一个类,而具体到哪条狗就叫对象。
2.1 类的定义与成员
类是用户自定义的数据类型,由数据成员(attributes)和函数成员(methods)组成。类用于指定对象的形式,它包含了数据表示法和用
于处理数据的方法。
&emsp类的定义包括类的名称、数据成员以及成员函数的声明与定义。C++中的类有三种访问控制方式:public、protected和private。这决定了类的成员在类外部的可访问性。
- public:所有成员可以在类外部直接访问。
- private:成员只能在类内部访问,类的外部不能直接访问这些成员。默认情况下,类的成员是private。
- protected:成员只能在类内部或派生类中访问,但不能在类的外部直接访问。
在C++中,类的成员可以分为几种不同的类型,每种类型都有其独特的用途和特性。了解这些成员类型有助于更灵活地设计和实现类。
- 数据成员(Data Members)是类中定义的变量,用于存储类对象的状态或属性。根据访问控制级别,它们可以是公有、私有或受保护的。
- 实例数据成员:这些是为每个对象单独存储的变量。每个对象都有自己的一份数据成员的副本。
- 静态数据成员:静态数据成员在所有对象之间共享,属于整个类而非某个对象。它们在类的定义中声明,并在类外部定义和初始化。
- 成员函数(Member Functions)是操作类对象的函数。根据它们的访问控制级别,它们也可以是公有、私有或受保护的。
- 实例成员函数:实例成员函数是操作类对象的普通函数。它们可以访问类的所有数据成员,包括私有数据成员。
- 静态成员函数:静态成员函数只能访问静态数据成员和其他静态成员函数,因为它们不依赖于具体的对象。静态成员函数通过类名调用,而不是通过对象调用。
- 内联函数(Inline Functions):内联函数是一种建议编译器将函数代码嵌入到调用点以减少函数调用开销的成员函数。这通常用于小型、频繁调用的函数。
- 构造函数:构造函数是类的特殊成员函数,用于初始化对象。构造函数在创建对象时(实例化时)自动调用,名称与类名相同,可以重载,没有返回类型(即使是 void 也不行)。
- 析构函数:析构函数是在对象生命周期结束时自动调用的特殊成员函数,用于清理资源。它的名称与类名相同,但前面带有“~”且不可以加参数,调用时释放内存,没有返回类型和参数。
#include <iostream> // 切记这里没有.h
using namespace std; // 命名空间
class Dog{
public:
Dog(){
cout<<"构造函数执行!"<<endl;
};
~Dog(){
cout<<"析构函数执行!"<<endl;
};
};
int main()
{
Dog dog;
cout<<"构造与析构函数示例"<<endl;
return 0;
}
- 常量成员(Const Members)指的是类中的常量数据成员和常量成员函数。
- 常量数据成员:一旦初始化后,其值不能再修改。它们必须通过初始化列表进行初始化。
- 常量成员函数:这些函数在调用过程中不会修改对象的状态,即不会修改任何数据成员。它们必须使用const关键字修饰。
// 示例:定义一个类
class ClassName {
public:
// 公有成员
int publicDataMember;
void publicMemberFunction(){
...
}
private:
// 私有成员
int privateDataMember;
void privateMemberFunction(){
...
}
protected:
// 受保护成员
int protectedDataMember;
void protectedMemberFunction(){
...
}
};
2.2 对象与实例化
类的实例称为对象。通过类定义可以创建多个对象,每个对象都有独立的属性空间,但共享类的成员函数。通过类来创建对象的过程称为实例化。可以使用构造函数来初始化对象的成员变量。
// 直接定义
ClassName test; // ClassName表示类,test表示对象
// 在堆里定义
ClassName* test = new ClassName;
// 删除对象(用于在堆里定义的方法)
delete test;
2.3 面向对象的三大特性
封装(Encapsulation)
封装是面向对象编程中的将数据与操作数据的函数捆绑在一起,并隐藏对象的内部实现细节,只对外暴露接口。
#include <iostream>
#include <string>
using namespace std;
class Dog{
public:
string name;
Dog(int i = 0){
total = i;
}
void addFood(int number) { // 利用addFood函数隐藏了实现细节
total = total + number;
}
int getFood() {
return total;
}
private:
int total;
};
int main()
{
Dog dog;
dog.name = "旺财";
dog.addFood(3);
dog.addFood(2);
cout<<dog.name<<"总共获得了"<<dog.getFood()<<"份食物"<<endl;
return 0;
}
多态(Polymorphism)
允许使用基类的指针或引用来调用派生类的方法。在运行时,C++会根据实际的对象类型来调用合适的方法,这被称为动态绑定或运行时多态。形成多态必须具备三个条件:
- 必须存在继承关系;
- 继承关系必须有同名虚函数(其中虚函数是在基类中使用关键字 virtual声明的函数,在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数);
- 存在基类类型的指针或者引用,通过该指针或引用调用虚函数。
继承(Inheritance)
类的继承是C++面向对象编程的核心特性之一,它允许一个类(子类或派生类)从另一个类(父类或基类)继承数据成员和成员函数,从而实现代码的重用和扩展。继承还支持多态性,使得子类对象可以被当作基类对象来使用。基类也称为父类或超类,是被继承的类;与之相对的派生类称为子类,是继承基类的类,派生类继承了基类的所有数据成员和成员函数。继承通过在派生类的声明中指定基类来实现。语法如下:
class BaseClass {
// 基类的成员
};
/* accessSpecifier:用于指定基类成员在派生类中的
访问权限,可以是public、protected或private。*/
class DerivedClass : accessSpecifier BaseClass {
// 派生类的成员
};
C++支持三种主要的继承方式,它们分别控制了基类成员在派生类中的访问级别——accessSpecifier:
5. 公有继承(Public Inheritance):基类的public成员在派生类中保持public,protected成员保持protected,private成员仍然无法访问。
6. 保护继承(Protected Inheritance):基类的public和protected成员在派生类中都变成protected,private成员仍然无法访问。
7. 私有继承(Private Inheritance):基类的public和protected成员在派生类中都变成private,private成员仍然无法访问。
2.4 重载与友元函数
2.4.1 重载
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。
- 函数重载
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。 - 运算符重载
运算符重载的实质就是函数重载或函数多态。运算符重载是一种形式的 C++多态。目的在于让人能够用同名的函数来完成不同的基本操作。要重载运算符,需要使用被称为运算符函数的特殊函数形式。
<返回类型说明符> operator <运算符符号>(<参数表>)
{
<函数体>
}
注:目前不可重载的运算符列表
2.4.2 友元函数
友元函数不是类的成员,但它可以访问类的私有成员。友元关系是单向的,友元函数往往用于操作多个类的私有数据或重载特定运算符。