C++ 笔记s

注意区分重载和覆写

  • 重载: overloaded 是指同名函数不同参数表可以重载
  • 覆写: overrided 是指用virtual 虚拟函数可以在子类里面去覆写(在继承的派生类中重新定义基类的虚函数)
  1. constructor 可以重载,不能被覆写(因为constructor在基类中不能是个虚函数)
  1. 而destructor 不能重载,只能覆写
  • 比如对于Class Shape,它的子类比如class Circleclass Rectangleclass Triangle,不同的子类在发生delete的时候,会调用不同的析构函数,比如Circle的析构函数,Rectangle的析构函数,Triangle的析构函数
  • 如果Shape的D'tor 不是virtual,而且,all_shapes[]数组里面的元素类型其实是一个Shape*型的指针,那么在delete的时候,只会调用基类的析构函数,而不会调用子类的析构函数

虚函数&纯虚函数

=0

在 C++ 中,virtual 函数后面写 = 0 是用来定义纯虚函数。纯虚函数是没有具体实现的虚函数,它的主要作用是让基类成为抽象类,从而无法直接实例化该类,必须在派生类中实现这个函数。
例如:
这种设计用于创建一种接口,要求派生类必须提供具体实现。

不写 =0

如果一个 virtual 函数不写 = 0,那么它不是纯虚函数,而是一个普通的虚函数。这意味着基类可以为该函数提供一个默认的实现,派生类可以选择是否重写该虚函数。

区别

  • 虚函数:基类可以有实现,派生类可以选择重写,也可以使用基类的实现。
  • 纯虚函数 (= 0):基类没有实现,强制要求派生类必须提供实现。基类无法被实例化,只能作为抽象基类使用。
示例:
相比之下,纯虚函数则需要派生类必须实现:

2024-11-10

Copy Constructor

Problem: 在赋值一个对象的时候,对象的一个属性——vector,其中vector的内容没有被复制过去,而是只复制了一个空vector过去

getline() 函数

  • std::getline(istream& is, std::string& str, char delim = '\n');
这是getline()函数的标准声明,其中is是输入流,str是读入的string,delim是结束读取的字符标识(默认是\n) > 其中读取到delim的时候并不会将它读入到str中,而是会丢弃,也就是恰好读到delim

map

  • map的底层是红黑树map中的pair在iterator顺序的排序上,是 只按照第一个键值的大小 进行排序
所以,这样的排序有个条件:
第一个键值的类型,必须是 可比的: map底层默认使用 operator <来比较,所以这个first-key的类型必须能通过<进行比较
(不论你是int型本身可以比,还是你进行了操作符重载) 如果无法比较则不会排序

2024-11-11

using 创建别名

在 C++ 中,using 关键字的主要用途之一是为类型创建别名。它是 typedef 的替代方案,语法更清晰,特别是对于复杂的类型和模板更容易使用和理解。以下是几种典型用法:

1. 简单类型别名

using 用于为复杂的类型创建别名,简化代码。
语法
示例
这样你就可以用 IntVector 代替 std::vector<int> 来声明变量,简化代码。

2. 函数指针类型别名

using 非常适合为复杂的类型(如函数指针)创建别名,简化声明。
示例
作用FuncPtr 作为函数指针类型的别名,简化了复杂的函数指针声明。

3. 模板类型别名

为模板类型创建别名,适用于复杂的模板组合。
示例
作用StringMap<int> 简化了 std::map<std::string, int>,使代码更易读。

typedef 对比

  • using 更清晰和直观,尤其在涉及模板或函数指针时:
    • typedef std::vector<int> IntVector; 使用 typedef
    • using IntVector = std::vector<int>; 使用 using
using 的语法更加符合现代 C++ 编程风格,简化了代码的书写和理解。

总结

  • 简单类型别名using NewTypeName = ExistingType;
  • 函数指针别名:为复杂的函数指针类型创建易于理解的别名。
  • 模板类型别名:为模板类型提供简化的名称,尤其适合复杂组合。
  • 推荐使用:相比 typedefusing 更清晰,尤其适合现代 C++。
通过 using,你可以让代码更加简洁、直观,提升代码的可读性和维护性。

使用自定义类in STL的时候你需要考虑的

notion image

比较操作符重载

当我们的自定义类在STL中需要比较的时候,我们会去重载比较操作符例如:(map中的键值使用的是自定义类型; map中的pair是按第一个键值的大小进行排序的,从小到大,map的底层是红黑搜索树)
  • 在定义比较类型的操作符的时候,可以只定义一个逻辑正确的<, 编译器会基于此重载,自己推断>=等一系列比较操作符

Pitfalls易错点

notion image
  • li被erase后就是一个野iters指针,不能再++li 但是erase函数本身会返回它erase的元素后面的一个的iters,使用可以用li = L.erase(li);来向下++
notion image
  1. 两个> >这个中间要个空格,否则可能被编译器识别为移位符号
  1. map的内嵌里面是pair,如果编译器里面有报pair的错误可能就是在map上的错误
  • 查看是否有一个元素orAny类似的
  1. foo.contains("Bob);
  1. foo.count() == 0
  1. foo.empty() == 1

auto类型推断

在 C++ 中,auto 是一个关键字,能够让编译器根据初始值的类型来自动推断变量的类型。这使得代码更加简洁、可读,尤其适合类型复杂或者模板类型较多的场景。auto 可以与 引用(&常量(const 以及 迭代器类型 结合使用,极大提高代码的可维护性和灵活性。

1. auto 的基本作用

auto 通过初始化表达式推断变量的类型,简化代码编写。
示例
  • 编译器根据赋值自动推断 xyz 的类型。

2. auto 和迭代器类型结合

auto 特别适合用来声明迭代器,因为迭代器类型通常非常复杂。使用 auto 可以简化迭代器声明。
示例:迭代器推断
解释
  • auto it = numbers.begin();:自动推断 it 的类型为 std::vector<int>::iterator,简化了迭代器的声明。

3. auto 和引用(&)的结合

auto 可以与引用 & 结合,创建对已有变量或容器元素的引用,这样对引用变量的操作会直接影响原变量。
示例:修改容器中的元素
解释
  • auto& numnum 被推断为 int&,是对 numbers 容器中元素的引用,因此 num 的修改会影响原始容器。
如果不使用引用(即 auto num),则 num 是一个拷贝,修改它不会影响原始容器。

4. auto 和常量(const)的结合

auto 可以结合 const,用于创建只读变量。这样可以确保变量的值不会被修改。
示例:const auto
解释
  • const auto 创建一个常量,防止变量被意外修改。

5. autoconst & 的结合

const & 用于只读引用,适合用于遍历容器中的元素时,确保不修改原始数据。
示例:只读遍历
解释
  • const auto& numnum 是对容器中元素的只读引用,保证遍历时不修改原始数据。

6. auto 的类型推断细节

  • 基本类型推断auto 会根据初始化值推断类型,例如 auto x = 5; 推断为 int
  • 指针和引用
    • 指针:如果初始值是指针,auto 会推断为指针类型。
    • 引用:如果初始值是引用,auto 会去掉引用,推断为基本类型,除非显式使用 &
示例:指针和引用推断
解释
  • auto x = ref;x 是一个副本,修改 x 不会影响 a
  • auto& y = ref;y 是引用,修改 y 会影响 a

总结

  • auto 的基本功能:自动推断变量的类型,减少代码的冗余。
  • auto 与迭代器:简化迭代器的声明,适用于遍历容器。
  • auto&(引用):用于对容器元素或变量进行直接操作,适合需要修改原始数据的情况。
  • const auto(常量):用于创建不可修改的变量,确保变量的只读属性。
  • const auto&(只读引用):用于只读访问容器的元素,防止在遍历中意外修改原始数据。
auto 在现代 C++ 中使代码更简洁和可读,特别是在使用复杂类型和模板时非常有用。结合 &const 使用,你可以灵活地控制变量的可修改性和引用性,提升代码的清晰度和安全性。
Loading...