到了找工作的时节,自己却还不会c++,那怎么能找到好工作呢?于是我试着学习一些c++。
学习笔记没有很强的逻辑性,而且有很强的个人属性。学到哪里写哪里,哪里不会记哪里。学习笔记本来应该针对现代c++,但是由于本科时候学c++已经是很久以前,学了之后又从来没有正经使用过,因此笔记里会有很多很基础的c++语法或面向对象编程的相关内容和相对高级的c++特性夹杂在一起。
- 由leetcode简单题27. 移除元素 - 力扣(LeetCode)想到的
- 参数使用引用传递容易理解:要修改原本的vector
-
vector在不使用引用传递时的行为是什么?
值传递,拷贝一份,开销大,因为是深拷贝(underlying memory也会拷贝,而不只是拷贝一份指针)
-
这个行为是谁来做的?是编译器还是类声明的拷贝构造函数?
不是编译器做的,是vector类自己声明的拷贝构造函数。
- 类的virtual function
- virtual function的调用是通过虚函数表来进行的(动态绑定,运行时多态)
- 每一个类对象的内存布局最前方保存了一个
vptr
,指向这个对象的虚函数表,虚函数表中再指向了具体的函数实现,虚函数在调用时,只与对象的实际类型有关,而与当前指针的静态类型无关 - 普通函数的调用是编译器直接在编译时决定了调用地址,
callq 0x1210
就可调用了,而不需要在运行时计算调用的地址。类的普通成员函数实质上是类的作用域中的全局函数,也就是和一个普通的函数没有区别,都是按照绝对的内存地址来调用的。类的内存布局里实际上只包含vptr
和成员变量,而不包含普通成员函数。 -
code example
#include <iostream> class A { public: virtual void foo(void) { std::cout<<"A foo"<<std::endl; } void bar(void) { std::cout<<"A bar"<<std::endl; } }; class B : public A { void foo(void) override { std::cout<<"B foo"<<std::endl; } void bar(void) { std::cout<<"B bar"<<std::endl; } }; int main() { B b; A* a = &b; a->foo(); // B foo a->bar(); // A bar return 0; }
push_back()
vsemplace_back()
push_back()
的参数如果是一个临时变量,会调用拷贝构造函数在末尾添加,如果是一个右值对象(MyClass()
),会调用移动构造函数在末尾添加emplace_back()
的参数如果是构造函数的参数如vec.emplace_back()
(空构造函数),会直接在末尾调用构造函数来添加对象emplace_back()
的参数如果是临时变量或者右值,则没有性能差异。
- Plain Old Data(POD)
- POD服从C ABI,可以进行二进制传递
- POD = trivial && standard_data_layout
is_pod_v(obj)
(deprecated)=is_standard_data_layout_v(obj)
&&is_trivial_v(obj)
- Trivial: 所有的构造、析构、移动、拷贝、赋值都是由编译器自动生成的,可以通过简单的memcpy来复制,memmove来移动
- Standard-layout:符合C语言中struct的标准内存排列
- 所有非静态成员都是相同的访问控制(public,private,protected)
- 没有虚函数或虚基类
- 所有非静态数据成员在基类中的顺序与其声明顺序一致。
- 不能有多个基类中包含相同类型的成员
- 虚基类(virtual base)
- 解决菱形继承问题
- Data member pointer
- data member pointer(数据成员指针)是指向类的成员变量(非静态成员)的指针。它允许通过指针来访问类的某个成员变量,而不是通过对象直接访问。
- 为什么不能是静态成员?data member pointer是一个相对于对象的内存布局的offset,而静态成员位于单独的内存区域,不和任何一个对象有关系。
-
code example
#include <iostream> using namespace std; class X { public: int a; void f(int b) { cout << "The value of b is "<< b << endl; } }; int main() { // declare pointer to data member int X::*ptiptr = &X::a; // declare a pointer to member function void (X::* ptfptr) (int) = &X::f; // create an object of class type X X xobject; // initialize data member xobject.*ptiptr = 10; cout << "The value of a is " << xobject.*ptiptr << endl; // call member function (xobject.*ptfptr) (20); }
- C++11不再允许将字符串字面量赋值给一个
char*
,而只能赋值给一个const char*
。- 原因: 字符串字面量本来就存储在不可写的内存区域,而
char*
是可写的,如果将字符串字面量赋值给char*
,可能会破坏这个不可写的内存区域,导致未定义行为。 - 如果需要一个可写的字符串,可以使用
std::string = "hello"
或者char str[] = "hello"
。std::string
实际上是通过重载=
操作符来实现的将.rodata段上的字符串拷贝到堆上,而char str[]
实际上是编译器进行的栈上内存分配和拷贝。
- 原因: 字符串字面量本来就存储在不可写的内存区域,而