C++ 碎碎念
1,C++的function用法:
之前学过lambda表达式第一次看见function觉得形式有一些不一样,原来也是属于C++11的一种用法。
参考:c++的function用法
2,C++的冒泡排序:十大经典排序算法
需要能够应对任何情况,int,float,自定义的等。
还是对模板编程不熟,对重载不熟,丫的。
#include <iostream>
#include <vector>
// 自定义类型示例
class Person {
public:
std::string name;
int age;
Person(std::string _name, int _age) : name(_name), age(_age) {}
bool operator>(Person& other) const {
return this->age > other.age;
}
};
// 通用模板函数实现冒泡排序
template<typename T>
void bubbleSort(std::vector<T>& arr) {
int n = arr.size();
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
std::swap(arr[j], arr[j + 1]);
}
}
}
}
int main() {
// 整型示例
std::vector<int> intArr = {5, 2, 8, 1, 9};
bubbleSort(intArr);
std::cout << "Sorted int array: ";
for (const auto& num : intArr) {
std::cout << num << " ";
}
std::cout << std::endl;
// 双精度浮点型示例
std::vector<double> doubleArr = {3.5, 1.2, 9.8, 2.1, 5.4};
bubbleSort(doubleArr);
std::cout << "Sorted double array: ";
for (const auto& num : doubleArr) {
std::cout << num << " ";
}
std::cout << std::endl;
// 自定义类型示例
std::vector<Person> personArr = {Person("Alice", 25), Person("Bob", 18), Person("Charlie", 30)};
bubbleSort(personArr);
std::cout << "Sorted person array by age: ";
for (const auto& person : personArr) {
std::cout << person.name << " (" << person.age << ") ";
}
std::cout << std::endl;
return 0;
}
模板编程
标准模板如下,但是不清晰的地方在于,如果参数列表没有指定typename那么还有什么方法指定。
template <typename type> ret-type func-name(parameter list)
{
// 函数的主体
}
符号重载
C++ 重载运算符和重载函数
C++ 输入/输出运算符重载
C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型。您可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。
在这里,有一点很重要,我们需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数。
C++ 友元函数
3,C++大端小段的判断和转换
大端(big endian):低地址存放高有效字节
小端(little endian):低字节存放地有效字节
C语言——printf打印字符串(关于数据在内存中存储格式的体现)
4,深拷贝与浅拷贝
面试官问:什么是浅拷贝和深拷贝?
浅拷贝
- 对于基本数据类型的成员变量,浅拷贝直接进行值传递,也就是将属性值复制了一份给新的成员变量
- 对于引用数据类型的成员变量,比如成员变量是数组、某个类的对象等,浅拷贝就是引用的传递,也就是将成员变量的引用(内存地址)复制了一份给新的成员变量,他们指向的是同一个事例。在一个对象修改成员变量的值,会影响到另一个对象中成员变量的值。
深拷贝
- 对于基本数据类型,深拷贝复制所有基本数据类型的成员变量的值
- 对于引用数据类型的成员变量,深拷贝申请新的存储空间,并复制该引用对象所引用的对象,也就是将整个对象复制下来。所以在一个对象修改成员变量的值,不会影响到另一个对象成员变量的值。
5,volatile关键字
C/C++ 中的 volatile
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
6,结构体直接赋值
看见有文章写直接用=号,但是仍然有些争论,不知道是指向同一个空间还是真正的拷贝。C语言用结构体给另一个同类型结构体赋值: 用等号即可
7,理解 C/C++ 中的左值和右值
还是不理解为什么说C++11引入的最强有力的特性就是右值引用和移动语义。
8,C++ 中的const
- const 相比于 define 的优势
const常量具有类型,编译器可以进行安全检查。#define宏定义没有数据类型,只是简单的字符串替换,不能进行安全检查。
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数。
const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。 - 指针常量与常量指针
指针常量:不能修改指针所指向的地址。在定义的同时必须初始化。
常量指针:不能修改指针所指向地址的内容。但可以改变指针所指向的地址。
如果const位于的右侧,则const就是修饰指针本身,即指针本身是常量(指针常量)
如果const位于的左侧,则const就是修饰指针所指向的变量,即指针指向常量(常量指针); - 类中的const
使用const关键字进行说明的成员函数,称为常成员函数。其只能调用常成员函数和常数据成员。- 常成员函数的特点:
不能修改类的任何非静态数据成员;
不能调用类的任何非const函数;
const关键字可以用于对重载函数的区分;
PS: 对于类中的const成员变量必须通过初始化列表进行初始化
- 常成员函数的特点:
9 C++ 基础提升
10 C++中把vector当作返回值的效率问题
因为在刷力扣时遇到了一种情况:我需要构造邻接表,邻接表是一种vector数组,存在两种情况,一种是我利用全局变量,不用传值或者返回值,一种情况是有vector返回值,我利用局部变量去承接这个返回值。这两种情况哪种更好呢,用局部变量去承接会不会产生问题?
参考:C++编程系列笔记(4)——C++函数直接返回std::vector效率高吗
这似乎是第一次实际的感受到了移动语义。
11 C++ 中的优先级队列的排序方式问题
C++ priority_queue(STL priority_queue)用法详解
默认是大根堆,但是针对自定义的类定义排序方式的时候发现了问题,一般的重载<排序是从小到大,但是此处确是相反。
可见在1514. 概率最大的路径,1631. 最小体力消耗路径
瞥见。
12 C++中的函数对象
也是在利用priority_queue优先级队列做题时发现的,GPT建议:STL 容器和算法原生支持使用仿函数作为自定义行为的接口
可见在此情况下优先使用。注:仿函数也称为函数对象。
13 C++的mutable和volatile
- mutable只能作用在类成员上,指示其数据总是可变的。不能和const 同时修饰一个成员,但能配合使用:const修饰的方法中,mutable修饰的成员数据可以发生改变,除此之外不应该对类/对象带来副作用。
- volatile用于修饰成员或变量,指示其修饰对象可能随时变化,编译器不要对所修饰变量进行优化(缓存),每次取值应该直接读取内存。由于volatile的变化来自运行期,其可以与const一起使用。