C++学习篇2
第一部分:内存分区模型
C++程序执行时内存分为四个区域:
- 代码区:存放函数体的二进制代码,操作系统管理,特点是共享和只读。
- 全局区:存放全局变量、静态变量和常量,程序结束后操作系统释放。
- 栈区:编译器自动分配释放,存放函数参数和局部变量,注意不要返回局部变量地址。
- 堆区:程序员分配和释放,使用
new
和delete
操作符,若不释放,程序结束时操作系统回收。
第二部分:引用
引用是给变量起别名的一种机制:
- 基本使用:通过
数据类型 &别名 = 原名
语法定义。 - 注意事项:
- 引用必须初始化。
- 初始化后,引用不可以改变。
- 引用作为函数参数:简化指针修改实参,通过
const
修饰防止误操作。 - 引用作为函数返回值:可以返回局部变量的引用,但不要返回局部变量的引用。
- 引用的本质:在C++内部实现是指针常量。
- 常量引用:修饰形参,防止误操作。
第三部分:函数提高
函数在C++中可以有默认参数、占位参数和重载:
- 函数默认参数:形参可以有默认值,简化函数调用。
- 函数占位参数:用于必须填补的位置参数。
- 函数重载:允许函数名相同,参数类型或个数不同,提高复用性。返回值不可以作为重载条件。
第四部分:类和对象
面向对象的三大特性:封装、继承、多态。
- 封装:将属性和行为作为一个整体,加以权限控制。
- 访问权限:
public
、protected
、private
。 struct
和class
的区别在于默认访问权限。- 成员属性设置为私有,控制读写权限,检测数据有效性。
- 访问权限:
- 对象的初始化和清理:
- 构造函数和析构函数:自动调用,完成对象初始化和清理。
- 构造函数分类:有参、无参(默认)、普通构造、拷贝构造。
- 拷贝构造函数调用时机:初始化新对象、值传递给函数参数、返回局部对象。
- 构造函数调用规则:用户定义有参构造函数时,编译器不提供默认无参构造函数。
- 深拷贝与浅拷贝:深拷贝重新申请空间,浅拷贝简单赋值。
- 初始化列表:使用构造函数初始化属性。
- 类对象作为类成员:对象成员的构造和析构顺序。
- 静态成员:静态成员变量和函数,所有对象共享。
4.1 封装
封装是面向对象编程的核心概念之一,它允许将数据(属性)和操作这些数据的代码(行为)组合在一起。封装提供了以下几个好处:
- 数据隐藏:通过访问修饰符(如
private
),可以隐藏类的内部实现细节。 - 数据保护:确保类的状态只能通过定义良好的接口进行修改。
- 易于使用:提供简单直观的接口供外部访问。
4.2 对象的初始化和清理
对象的生命周期包括创建和销毁两个阶段。C++提供了构造函数和析构函数来管理这两个阶段:
- 构造函数:在对象创建时自动调用,用于初始化对象的状态。
- 析构函数:在对象销毁前自动调用,用于执行清理工作,如释放分配的资源。
4.3 构造函数和析构函数
构造函数和析构函数是类的特殊的成员函数:
- 构造函数:没有返回类型,也没有参数(尽管可以有默认参数),其名称与类名相同。构造函数可以被重载,以支持不同的初始化方式。
- 析构函数:同样没有返回类型,名称前加
~
符号,用于对象生命周期结束时的清理。
4.4 拷贝构造函数
拷贝构造函数用于通过现有对象初始化新对象。它有三个特点:
- 参数:接受一个常量引用类型的参数。
- 用途:用于实现对象的深拷贝或浅拷贝。
- 调用时机:对象作为参数传递给函数、返回对象类型的函数值、使用对象初始化另一个对象时。
4.5 深拷贝与浅拷贝
- 浅拷贝:只复制对象的直接成员,如果对象包含指针,那么拷贝后两个对象将共享同一块内存。
- 深拷贝:为对象的每个成员创建新的内存,即使成员是指针,也会复制指针指向的内容。
4.6 初始化列表
初始化列表用于在构造函数中初始化类的成员变量。它提供了一种更高效、更清晰的方式来初始化对象。
4.7 静态成员
静态成员属于类本身,而不是类的任何特定对象:
- 静态成员变量:所有对象共享同一份数据。
- 静态成员函数:所有对象共享同一个函数,只能访问静态成员变量。
4.8 类对象作为类成员
当一个类的对象作为另一个类的成员时,成员对象的构造和析构顺序遵循特定的规则:先构造成员对象,后构造包含它的对象;析构顺序与之相反。
第五部分:C++对象模型和this指针
- 成员变量和成员函数分开存储,非静态成员变量属于对象。
this
指针是指向当前对象的指针,它允许成员函数访问和修改其所属对象的状态。this
指针的主要用例包括: - 区分成员变量和局部变量:当成员函数中的局部变量与成员变量同名时,使用
this
指针来区分。 - 返回对象本身:在成员函数中使用
return *this
可以返回对象的引用。 - 空指针访问成员函数:可以调用,但需注意
this
指针的使用。 const
修饰成员函数:常函数不可修改成员属性,mutable
关键字允许在常函数中修改。
第六部分:友元
友元是C++中的一个特殊概念,它允许非成员函数或另一个类的成员函数访问类的私有或保护成员:
- 全局函数作为友元:可以访问类的私有和保护成员。
- 类作为友元:友元类的所有成员函数都可以访问声明它的类的私有和保护成员。
- 成员函数作为友元:特定类的成员函数可以访问另一个类的私有和保护成员。
第七部分:运算符重载
运算符重载允许为自定义类型定义运算符的行为:
- 加号运算符:允许两个自定义类型对象相加。
- 左移运算符:通常与
ostream
结合使用,用于输出自定义类型。 - 递增运算符:可以重载为前置或后置,改变对象的状态。
- 赋值运算符:需要特别关注深浅拷贝问题。
- 关系运算符:允许比较两个自定义类型对象。
- 函数调用运算符:允许对象像函数一样被调用,称为仿函数。
第八部分:继承
继承减少重复代码,基类和派生类的概念:
- 继承的基本语法:
class 子类 : 继承方式 父类
。 - 继承方式:公共继承、保护继承、私有继承。
- 继承中的对象模型:私有成员也被继承。
- 继承中的构造和析构顺序:先父类构造,后子类构造,析构相反。
- 继承同名成员处理:子类可以直接访问,父类需要加作用域。
- 继承同名静态成员处理:通过对象或类名访问。
- 多继承语法:可能引发同名成员问题,使用作用域区分。
- 菱形继承:两个派生类继承同一个基类,一个类继承这两个派生类。
第九部分:多态
9.1 多态的基本概念
多态是面向对象编程的核心特性之一,它允许派生类重写基类的虚函数,使得同一个接口可以有不同的实现。多态使得代码更加灵活和可扩展。
9.2 静态多态与动态多态
- 静态多态:也称为早绑定,编译时确定调用哪个函数,如函数重载和运算符重载。
- 动态多态:也称为晚绑定,运行时确定调用哪个函数,主要通过虚函数实现。
9.3 虚函数和抽象类
- 虚函数:在基类中使用
virtual
关键字声明的函数,可以被子类重写。 - 抽象类:包含至少一个纯虚函数的类,不能实例化,通常作为其他类的接口。
9.4 多态的使用
多态的使用通常涉及以下几个步骤:
- 定义基类:包含虚函数。
- 派生类:重写基类的虚函数。
- 使用基类指针或引用:指向派生类对象,实现多态。
9.5 纯虚函数和抽象类
- 纯虚函数:使用
virtual 返回类型 函数名() = 0;
声明,没有实现体。 - 抽象类:包含纯虚函数的类,不能被实例化,通常用作接口。
9.6 虚析构和纯虚析构
- 虚析构:在基类中声明
virtual ~类名() {}
,确保通过基类指针删除派生类对象时正确调用派生类的析构函数。 - 纯虚析构:声明为
virtual ~类名() = 0;
,使得类成为抽象类,强制派生类提供析构函数的定义。
第十部分:文件操作
10.1 文件概述
文件是数据的持久化存储,C++提供了丰富的文件操作API。
10.2 文件类型
- 文本文件:以ASCII码形式存储,可读性强。
- 二进制文件:以二进制形式存储,存储效率高,但可读性差。
10.3 文件操作类
- ofstream:用于文件写操作。
- ifstream:用于文件读操作。
- fstream:支持文件的读写操作。
10.4 文本文件操作
10.4.1 写文件
写文件的基本步骤:
- 包含头文件
#include <fstream>
。 - 创建
ofstream
对象。 - 使用
open
函数打开文件。 - 使用
<<
运算符写入数据。 - 使用
close
函数关闭文件。
10.4.2 读文件
读文件的基本步骤:
- 包含头文件
#include <fstream>
。 - 创建
ifstream
对象。 - 使用
open
函数打开文件。 - 读取数据,可以使用
>>
运算符或成员函数如getline
。 - 使用
close
函数关闭文件。
10.5 二进制文件操作
10.5.1 写文件
二进制写文件使用ofstream
对象的write
成员函数:
ofstream ofs("filename", ios::binary);
char buffer[] = {...}; // 数据缓冲区
ofs.write(buffer, sizeof(buffer));
10.5.2 读文件
二进制读文件使用ifstream
对象的read
成员函数:
ifstream ifs("filename", ios::binary);
char buffer[100]; // 数据缓冲区
ifs.read(buffer, sizeof(buffer));
10.6 文件打开模式
- ios::in:打开文件用于读取。
- ios::out:打开文件用于写入。
- ios::app:每次写操作后定位到文件末尾。
- ios::trunc:如果文件存在,先截断文件再写入。
- ios::binary:以二进制模式打开文件。
10.7 文件流的状态检查
使用is_open
函数检查文件是否成功打开,使用eof
检查是否到达文件末尾。
10.8 文件流的异常处理
C++标准库提供了一套异常处理机制,可以在文件操作中捕获和处理异常。