C++期末考试总结(图片版)

流是数据池的一种抽象表示,在程序执行时,每个流都关联着某个设备关联着数据源的流就是输入流,关联着数据目的地的就是输出流

插入运算符<<在编写涉及输入的程序时使用提取运算符>> 

return 0;非0返回值是否起作用取决于调用该程序的系统

空白符:空格符、回车符、换页符、横向制表符、纵向制表符

标准C++不限制标识符的长度,但是只有前三十一个字符是有效的。此外标识符长度受C++编译系统的限制

用户自定义的标识符最好不要用系统定义的标识符如main sqrt等以及预处理指令中涉及的include、define等等

虽然允许给这些标识符定义新的意义但会使其失去原来的作用,从而产生歧义

按照不同的表示形式,可以将常量分为字面常量、符号常量、常值变量

字面常量也称直接常量,他们不存储在变量内存中

习惯上符号常量的标识符用大写字母,变量标识符用小写字母

常值变量是和符号变量相对的,常值变量需要通过const关键词定义,常量只能读不能修改,并且定义时必须初始化

推荐使用const而不是define预处理指令:1、const可以定义数据类型,以提高类型的安全性2、const既然是变量那么就有地址,适用面更广

八进制数通常是无符号的

C++中定义了三种整数类型,short int(2),int(4),long int(4)(在32位计算机中)

浮点型也叫实型,包括单精度,双精度,和扩展的双精度

float型只能提供6位有效数字

如果小数点前面或者后面的数为0,可以省略一个0,但不能同时省略,而且必须有小数点

C++中允许浮点数使用后缀fFlL,没有后缀的默认为double型

广义地讲,C++语言字符集中任何一个字符均可以用转义字符来表示

字符值是以ascll码的形式存放在变量的内存单元中的,因此可以把字符变量看成整形量,C++语言允许把字符变量按整形变量输出,也允许把整形量按照字符量输出,允许对整形变量赋以字符值,允许对字符变量赋以整型值

switch语句中判断表达式括号应具有整型值,一般为整型,字符型,枚举类型或者表达式。case后面的标号为常量表达式,其值必须是整型、字符型或枚举类型

main函数可以出现在任何位置,所有函数都是平行定义的,在一个函数内部不允许定义另外函数

有三种方式调用函数:函数表达式、函数语句、函数实参

函数值的类型和函数定义中函数类型应保持一致,如果两者不一致,则以函数类型为准,自动进行类型转换

函数调用过程中实际参数与形式参数匹配,首先精确匹配实参与形参的数据类型,如果不能精确匹配,则按照由低精度到高精度转换规则匹配(实际参数类型转换到形式参数类型),另外函数重载不能靠函数返回值类型来区别,因为调用时体现不出函数返回值类型

默认参数也称为缺省参量,函数定义中的每一个参数都可以拥有一个默认值,如果在函数调用中没有为对应默认值的参量提供实参数据,系统就直接使用默认值

默认参数定义的顺序为自右向左,即如果一个参数设定了缺省值时,其右边的参数都要有缺省值

定义数组中方括号中的常量表达式表示数组元素的个数,也称数组的长度

数组名实际上就是第一个元素的地址,不代表变量值,而且是一个常量不能修改

引用数组越界时,运行时并不报错(c++语言为了提高效率,因为检查是否越界要占用系统时间),但是使用时有可能会破坏其他数据

对于局部变量的数组,数组元素定义后,若不赋值,则值为由编译器指定的无意义的值

数组的初始化是在编译阶段进行的,这样将减少运行时间,提高效率

C++语言对数组的初始化赋值时,当{ }中的值个数少于元素个数时,未赋值的部分会置为与类型相关的特定值整型为0;浮点型为0.0;字符型为‘\0’

实际的硬件存储器是连续编址的,二维数组是按一维线性排列的

 声明引用时,必须同时对其进行初始化

声明一个引用,不是新定义了一个变量,他只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元

对引用的限制:不能引用一个引用变量;不能创建一个指向引用的指针;不能建立数组的引用,因为数组是一个由若干个元素组成的集合,所以无法建立一个数组的别名

 常引用:不能通过引用对目标变量的值进行修改

用引用返回一个函数值的最大好处是:在内存中不产生被返回值的副本

在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要是用在函数参数传递中,解决大块数据或对象传递效率低和空间开销大的问题

在引用传递函数的参数,能保证参数传递时不产生副本,提高传递效率,且通过const的使用,保证了引用传递的安全性

通过引用,会让函数返回值成为左值,在运算符重载部分会经常使用

类是面向对象技术的核心机制,是面对对象设计中对具有相同或相似性质的对象的抽象,是对数据和操作进行封装的载体

用同一类定义的对象,各对象的成员数据类型和成员函数类型完全相同,可以整体赋值

 构造函数的作用:为对象分配空间并初始化、为数据成员赋值;请求其他资源

数组成员不能在初始化列表中初始化、static数据成员不能在初始化列表中初始化、非static的const数据成员必须在初始化列表中初始化

写在初始化列表中等价于简单变量的定义并且初始化;写在函数体中等价于简单变量先定义后赋值,写在初始化列表中的效率更高

在类中定义的析构函数在对象生命周期结束时自动执行,完成清理内存工作;并可以执行指定的其他操作

析构函数是一种在结束对象调用时自动执行的特殊的成员函数,一个类中只能定义一个析构函数

拷贝构造函数名和类名相同,参数是本类对象的引用,拷贝构造函数没有返回值

 

 

 

 浅拷贝会带来堆区的内存重复释放

记住如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

浅拷贝只是拷贝对象空间,深拷贝可以拷贝对象空间之外申请的资源

如果构造函数有多个参数,则不能用在定义数组时直接提供所有实参的方法 

成员函数通过this指针接收调用对象的地址,所以每个成员函数(非静态)都有一个隐含的指针变量this

静态数据成员存储在全局数据区,静态数据成员在程序一开始运行时就必须存在,静态数据成员定义时要分配空间,因此,需要在类外初始化静态数据成员,即使静态数据成员是私有的也要在类外初始化

既可以通过对象访问静态数据成员,也可以通过类访问静态数据成员 

静态成员函数由于不与任何的对象相联系,,他无法访问属于类对象的非静态数据成员也无法访问非静态成员函数,他只能调用其余的静态成员函数,

静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数,但不能访问非静态成员函数和非静态数据成员,而非静态成员函数可以任意的访问静态成员函数和静态数据成员

当其他类对象作为本类成员,构造时候先构造类对象,再构造自身,析构的顺序与构造相反

 多重括号间不用加空格

 

 尽量不要使用大小写与下划线混排的方式

 静态成员变量也是有访问权限的

静态成员函数只能访问静态成员变量

 this指针的本质是指针常量,指向是不可以修改的

 友元关系是不能传递的

运算符重载也可以发生函数重载

C++规定,运算符操作数都为基本类型时不能重载

 

 有些特殊的运算符只能重载为成员函数,如下标运算符和赋值运算符,而有些运算符只能重载为非成员函数,如流插入和流提取运算符

静态成员函数只能调用静态的成员比那辆

想要创建一个派生类的对象,首先会创建一个基类的对象,然后在这个基类对象的基础上构造一个派生类的对象,这两个对象的内存是在一起的 

组合单继承派生类的构造函数执行初始化的顺序与构造函数中声明的初始化列表的顺序无关,系统一定会按照先祖先再客人后自己的顺序执行

影响多重继承基类构造函数调用顺序的是派生类定义时的继承列表

不同类型间的自动转换和赋值被称为赋值兼容

派生类对象可以直接赋值给基类对象,派生类对象可以初始化基类的引用

派生类对象的地址可以赋值给基类的指针

一个函数的形式参数如果是一个基类的引用,在实际调用该函数时,可以传递一个派生类对象来代替基类对象

函数重载时需要关联,因为在编译时即可确定其调用的函数是哪个类的哪个函数,所以称这种关联为静态关联,由于他是在运行前进行的关联,故又称为早期关联

构造函数是不能声明为虚函数的

纯虚函数是在声明虚函数时被初始化为0的函数,纯虚函数是没有函数体的

带有纯虚函数的类是抽象类,抽象类只能作为基类来使用,抽象类是没有办法实例化的

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放,通过文件可以将数据持久化

 

自动类型推导必须推导出一致的数据类型 

模版参数说明的每个类型参数必须在函数定义的形式参数表中至少出现一次 

 

引用空间大小和指针一样 

静态成员变量和静态成员函数既可以通过类对象调用,又可以通过类名进行调用 

静态成员函数只能调用静态成员变量

 

 

 

 const变量必须在声明时初始化

string 类定义隐藏了字符串的数组性质,能够像处理普通变量那样处理字符串。

 

 

在父类中定义一个static变量,由他继承得到两个子类,那么父类和两个子类中的这个变量是指同一个变量 

报错,有常量就不能作为左值了

 

析构函数没法被重载,函数模版可以被重载 

在C++中如果两个函数cosnt或者volatitle限定不同,那么这两个函数不能重写

成员函数和全局函数位于不同作用域,两个函数没法重载

如果一个变量在class中没有指定访问修饰符,那么默认为private

动态多态是通过虚函数和继承实现的

使用resize重新指定容器长度的时候如果容器变短则末尾超出容器长度的元素会被删除 

 

 

 "指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。

是不是使用了虚继承的基类就是虚基类

在类中定义的变量被称为“成员变量”(Member Variables)

类模版没有自动推导,所以必须指定类型,可以发生隐式类型转换

类模板中的成员函数并不是一开始就创建的,在调用时才去创建

构造函数的调用顺序
父类构造函数:如果存在继承关系,首先调用基类(父类)的构造函数。
成员变量构造函数:在进入派生类(子类)构造函数的函数体之前,成员变量的构造函数按照它们在类中声明的顺序被调用。
派生类构造函数体:执行派生类构造函数的函数体。
析构函数的调用顺序
派生类析构函数体:首先执行派生类析构函数的函数体。
成员变量析构函数:在离开派生类析构函数的函数体后,成员变量的析构函数按照它们构造的逆序被调用。
父类析构函数:最后调用基类(父类)的析构函数。

当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型如果不指定,编译器无法给子类分配内存如果想灵活指定出父类中T的类型,子类也需变为类模板

//类模板继承类模板 ,可以用T2指定父类中的T类型 template class Son2 :public Base { 

是的,友元函数可以在类内实现,但这样做的话,友元函数实际上就变成了一个非成员函数,并且是全局的。在类内定义友元函数时,它的定义不属于类的一部分,而是在全局命名空间中。不过,通常情况下,友元函数是在类外定义的,以保持类定义的清晰和易于维护。

序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

 

 因此链表list中的迭代器只支持前移和后移,属于双向迭代器

采用动态存储分配,不会造成内存浪费和溢出链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素 list的缺点:链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大 List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。总结:STL中List和vector是两个最常被使用的容器,各有优缺点、

 reserve 是 std::vector 的成员函数,用于预留一定的存储空间。它并不会改变 vector 的大小(即 size),而是改变容量(即 capacity),从而避免在插入新元素时频繁的内存重新分配和数据拷贝。

std::vector 和 std::list 是 C++ 标准库中的两种常用容器,它们各自有不同的实现和用途。以下是它们各自的优点和缺点:
std::vector
std::vector 是一个动态数组,提供了高效的随机访问和在末尾进行插入和删除操作的能力。
优点
随机访问快:
std::vector 支持常数时间复杂度的随机访问(通过 operator[] 和 at)。
适合频繁需要通过索引访问元素的场景。
内存连续:
std::vector 在内存中是连续存储的,这意味着缓存性能很好。
适合需要与低级别数组进行交互的场景。
动态调整大小:
std::vector 会根据需要自动调整其容量,但这一过程需要重新分配内存和复制元素。
可以通过 reserve 方法预先分配内存以避免频繁的重新分配。
末尾插入和删除快:
在末尾插入和删除元素的时间复杂度为常数时间 O(1)。
缺点
中间插入和删除慢:
在中间插入或删除元素时,需要移动插入位置后的所有元素,时间复杂度为线性时间 O(n)。
可能导致内存重新分配:
当容量不够时,插入新元素可能导致整个 vector 的内存重新分配,并复制所有现有元素,影响性能。
std::list
std::list 是一个双向链表,提供了高效的插入和删除操作,尤其是在中间进行操作时。
优点
中间插入和删除快:
在任意位置插入或删除元素的时间复杂度为常数时间 O(1)(前提是已知位置的迭代器)。
适合需要频繁在中间插入和删除元素的场景。
不需要连续内存:
std::list 的每个元素都是独立分配的,不需要连续的内存块。
适合内存分布零散的情况。
稳定的迭代器:
插入或删除元素后,其他元素的迭代器不会失效。
缺点
随机访问慢:
不支持常数时间的随机访问,访问元素时需要从头开始遍历,时间复杂度为线性时间 O(n)。
不适合需要频繁通过索引访问元素的场景。
内存开销大:
每个元素需要额外的指针来维护链表结构,增加了内存开销。
对于存储小对象来说,内存开销尤其显著。
缓存性能差:
元素分散存储,缓存命中率低,影响性能。
示例代码
以下是使用 std::vector 和 std::list 的简单示例:
cpp复制代码
#include <iostream>
#include <vector>
#include <list>

void useVector() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 随机访问
    std::cout << "Vector element at index 2: " << vec[2] << std::endl;

    // 末尾插入
    vec.push_back(6);

    // 中间插入
    vec.insert(vec.begin() + 2, 10);

    // 输出元素
    for (int elem : vec) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

void useList() {
    std::list<int> lst = {1, 2, 3, 4, 5};

    // 末尾插入
    lst.push_back(6);

    // 中间插入
    auto it = lst.begin();
    std::advance(it, 2);
    lst.insert(it, 10);

    // 输出元素
    for (int elem : lst) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

int main() {
    useVector();
    useList();
    return 0;
}
总结
std::vector 适合需要高效随机访问和在末尾进行插入和删除操作的场景。由于内存是连续的,它的缓存性能很好。
std::list 适合需要频繁在中间插入和删除元素的场景。它不支持随机访问,但可以在任意位置进行高效的插入和删除操作。
根据具体的需求选择合适的容器,可以在性能和内存使用上取得最佳平衡。

### `std::vector`

`std::vector` 是一个动态数组,提供了高效的随机访问和在末尾进行插入和删除操作的能力。

#### 优点

1. **随机访问快**:
   - 支持常数时间复杂度的随机访问(通过 `operator[]` 和 `at`)。
   - 适合频繁需要通过索引访问元素的场景。

2. **内存连续**:
   - 在内存中是连续存储的,这意味着缓存性能很好。
   - 适合需要与低级别数组进行交互的场景。

3. **动态调整大小**:
   - 会根据需要自动调整其容量,但这一过程需要重新分配内存和复制元素。
   - 可以通过 `reserve` 方法预先分配内存以避免频繁的重新分配。

4. **末尾插入和删除快**:
   - 在末尾插入和删除元素的时间复杂度为常数时间 O(1)。

#### 缺点

1. **中间插入和删除慢**:
   - 在中间插入或删除元素时,需要移动插入位置后的所有元素,时间复杂度为线性时间 O(n)。

2. **可能导致内存重新分配**:
   - 当容量不够时,插入新元素可能导致整个 `vector` 的内存重新分配,并复制所有现有元素,影响性能。

### `std::list`

`std::list` 是一个双向链表,提供了高效的插入和删除操作,尤其是在中间进行操作时。

#### 优点

1. **中间插入和删除快**:
   - 在任意位置插入或删除元素的时间复杂度为常数时间 O(1)(前提是已知位置的迭代器)。
   - 适合需要频繁在中间插入和删除元素的场景。

2. **不需要连续内存**:
   - 每个元素都是独立分配的,不需要连续的内存块。
   - 适合内存分布零散的情况。

3. **稳定的迭代器**:
   - 插入或删除元素后,其他元素的迭代器不会失效。

#### 缺点

1. **随机访问慢**:
   - 不支持常数时间的随机访问,访问元素时需要从头开始遍历,时间复杂度为线性时间 O(n)。
   - 不适合需要频繁通过索引访问元素的场景。

2. **内存开销大**:
   - 每个元素需要额外的指针来维护链表结构,增加了内存开销。
   - 对于存储小对象来说,内存开销尤其显著。

3. **缓存性能差**:
   - 元素分散存储,缓存命中率低,影响性能。

### `std::set`

`std::set` 是一个有序集合,存储唯一的元素,并自动对其进行排序。

#### 优点

1. **有序性**:
   - 自动对元素进行排序,支持按顺序遍历。
   - 适合需要保持元素有序的场景。

2. **唯一性**:
   - 确保元素唯一性,不允许重复元素。

3. **高效查找**:
   - 支持高效的查找、插入和删除操作,时间复杂度为 O(log n)。

#### 缺点

1. **不支持随机访问**:
   - 不支持通过索引访问元素,随机访问性能差。

2. **内存开销**:
   - 每个元素需要额外的内存来维护树结构,内存开销相对较大。

### `std::map`

`std::map` 是一个有序的键值对容器,基于红黑树实现。

#### 优点

1. **有序性**:
   - 自动对键值对按键进行排序,支持按键顺序遍历。

2. **高效查找**:
   - 支持高效的查找、插入和删除操作,时间复杂度为 O(log n)。

3. **键值对存储**:
   - 适合需要根据键快速查找值的场景。

#### 缺点

1. **不支持随机访问**:
   - 不支持通过索引访问元素,随机访问性能差。

2. **内存开销**:
   - 每个键值对需要额外的内存来维护树结构,内存开销相对较大。

### 总结

- **`std::vector`**:适合需要高效随机访问和末尾插入/删除的场景,但在中间插入/删除时性能较差。
- **`std::list`**:适合需要频繁在中间插入/删除的场景,但不支持高效随机访问,内存开销较大。
- **`std::set`**:适合需要存储唯一有序元素的场景,但不支持随机访问,内存开销较大。
- **`std::map`**:适合需要存储有序键值对并根据键高效查找的场景,但不支持随机访问,内存开销较大。

根据具体的需求选择合适的容器,可以在性能和内存使用上取得最佳平衡。

重载(Overloading)和重写(Overriding)是面向对象编程中两个重要的概念,它们用于实现多态性和方法的多样性,但在应用场景和实现方式上有明显的区别。
重载(Overloading)
重载是指在同一个作用域内定义多个函数,这些函数具有相同的名称,但参数列表不同。重载通常发生在同一个类中,可以用于普通函数和运算符函数。
特点
函数名相同:重载的函数具有相同的名称。
参数列表不同:重载函数的参数类型、参数个数或参数顺序至少有一个不同。
返回类型无关:重载与函数的返回类型无关,即仅通过返回类型不同不能实现重载。
编译期多态:重载是编译期多态,具体调用哪一个重载函数在编译时就确定了。

重载(Overloading)和重写(Overriding)是面向对象编程中两个重要的概念,它们用于实现多态性和方法的多样性,但在应用场景和实现方式上有明显的区别。
重载(Overloading)
重载是指在同一个作用域内定义多个函数,这些函数具有相同的名称,但参数列表不同。重载通常发生在同一个类中,可以用于普通函数和运算符函数。
特点
函数名相同:重载的函数具有相同的名称。
参数列表不同:重载函数的参数类型、参数个数或参数顺序至少有一个不同。
返回类型无关:重载与函数的返回类型无关,即仅通过返回类型不同不能实现重载。
编译期多态:重载是编译期多态,具体调用哪一个重载函数在编译时就确定了。

区别总结
定义范围:
重载:发生在同一个类中。
重写:发生在基类和派生类之间。
函数签名:
重载:函数名相同,但参数列表必须不同。
重写:函数名、参数列表和返回类型必须与基类中的虚函数相同。
目的:
重载:实现编译期多态,提高函数名的可读性和可用性。
重写:实现运行期多态,允许派生类提供基类函数的具体实现。
关键词:
重载:无特殊关键词,仅通过参数列表的不同来实现。
重写:基类函数必须是虚函数,派生类函数可以使用 override 关键字(C++11 起)来显式标识重写。
通过理解重载和重写的区别,可以更好地设计和使用面向对象程序,实现灵活且高效的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值