C++ 笔记s
注意区分重载和覆写
- 重载: overloaded 是指同名函数不同参数表可以重载
- 覆写: overrided 是指用virtual 虚拟函数可以在子类里面去覆写(在继承的派生类中重新定义基类的虚函数)
- constructor 可以重载,不能被覆写(因为constructor在基类中不能是个虚函数)
- 而destructor 不能重载,只能覆写
- 比如对于
Class Shape
,它的子类比如class Circle
,class Rectangle
,class 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;
- 函数指针别名:为复杂的函数指针类型创建易于理解的别名。
- 模板类型别名:为模板类型提供简化的名称,尤其适合复杂组合。
- 推荐使用:相比
typedef
,using
更清晰,尤其适合现代 C++。
通过
using
,你可以让代码更加简洁、直观,提升代码的可读性和维护性。使用自定义类in STL的时候你需要考虑的
比较操作符重载
当我们的自定义类在STL中需要比较的时候,我们会去重载比较操作符例如:(map中的键值使用的是自定义类型; map中的pair是按第一个键值的大小进行排序的,从小到大,map的底层是红黑搜索树)
- 在定义比较类型的操作符的时候,可以只定义一个逻辑正确的
<
, 编译器会基于此重载,自己推断>
和=
等一系列比较操作符
Pitfalls易错点
- li被erase后就是一个野iters指针,不能再++li
但是erase函数本身会返回它erase的元素后面的一个的iters,使用可以用
li = L.erase(li);
来向下++
- 两个> >这个中间要个空格,否则可能被编译器识别为移位符号
- map的内嵌里面是pair,如果编译器里面有报pair的错误可能就是在map上的错误
- 查看是否有一个元素orAny类似的
foo.contains("Bob);
foo.count() == 0
foo.empty() == 1
auto类型推断
在 C++ 中,
auto
是一个关键字,能够让编译器根据初始值的类型来自动推断变量的类型。这使得代码更加简洁、可读,尤其适合类型复杂或者模板类型较多的场景。auto
可以与 引用(&
)、常量(const
) 以及 迭代器类型 结合使用,极大提高代码的可维护性和灵活性。1. auto
的基本作用
auto
通过初始化表达式推断变量的类型,简化代码编写。示例:
- 编译器根据赋值自动推断
x
、y
、z
的类型。
2. auto
和迭代器类型结合
auto
特别适合用来声明迭代器,因为迭代器类型通常非常复杂。使用 auto
可以简化迭代器声明。示例:迭代器推断:
解释:
auto it = numbers.begin();
:自动推断it
的类型为std::vector<int>::iterator
,简化了迭代器的声明。
3. auto
和引用(&
)的结合
auto
可以与引用 &
结合,创建对已有变量或容器元素的引用,这样对引用变量的操作会直接影响原变量。示例:修改容器中的元素:
解释:
auto& num
:num
被推断为int&
,是对numbers
容器中元素的引用,因此num
的修改会影响原始容器。
如果不使用引用(即
auto num
),则 num
是一个拷贝,修改它不会影响原始容器。4. auto
和常量(const
)的结合
auto
可以结合 const
,用于创建只读变量。这样可以确保变量的值不会被修改。示例:
const auto
:解释:
const auto
创建一个常量,防止变量被意外修改。
5. auto
和 const &
的结合
const &
用于只读引用,适合用于遍历容器中的元素时,确保不修改原始数据。示例:只读遍历:
解释:
const auto& num
:num
是对容器中元素的只读引用,保证遍历时不修改原始数据。
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
使用,你可以灵活地控制变量的可修改性和引用性,提升代码的清晰度和安全性。