cmake入门了解
编译器是什么?
编译器读取源代码,生成二进制文件
什么是IDE(集成开发环境)?如VC6.0、VS2012、CodeBlocks等。核心还是编译器,为了方便,全部免去命令形式的操作,取而代之的友好的界面,一键编译等方便的操作。
从.cpp文件到.exe过程
编写C++源代码,并保存为 .cpp
文件 -->编译器编译源代码文件,生成一个目标文件-->将目标文件与库一起链接(使用链接器如 ld
、g++
、clang++
或 link.exe)
-->生成可执行文件.exe并运行。
make、makefile、cmake、cmakelist的关系:
make作为工具对makefile中的命令进行编译和链接。makefile命令中就包含了调用gcc(也可以是别的编译器)去编译某个源文件的命令。cmake可以更加简单的生成makefile文件给上面那个make用。(最重要的是,可以跨平台生成对应平台能用的makefile。)cmake又要根据一个叫CMakeLists.txt文件去生成makefile。CMake通过CMakeLists.txt 文件来描述项目的构建过程,用户可以在这个文件中定义源代码、库、头文件、编译选项等信息,从而让CMake自动生成相应的构建文件。
https://i-blog.csdnimg.cn/blog_migrate/c9245cf4c2f0baac835c63ec1fa7e4b2.png
main函数是什么?
编程语言像C/C++/C#/JAVA 等main主函数作为程序入口,程序从main()主函数的第一句代码开始执行,直到main函数的最后一句代码程序运行结束,结束后程序退出,申请的所有内存空间等被回收。
c++为什么分为头文件和cpp文件
C++ 支持将接口(声明)和实现(定义)分开。头文件通常包含函数、类、模板等的声明,而源文件包含它们的具体实现。
提高编译效率:头文件通常只包含声明,它们可以在多个源文件中被包含而不需要重新编译,允许开发者在不同的源文件中重用相同的接口。
利于接口隐藏:头文件可以作为模块的接口,而源文件包含实现细节。这有助于隐藏实现细节,只暴露必要的接口给其他模块
动态库
C++内存管理:理解堆、栈、指针,避免内存泄漏
堆是一块用于动态分配内存的区域,存放的是通过new和delete关键字来分配和释放的对象。堆上的内存需手动管理,如果不及时释放,就会造成内存泄漏。
栈是一种自动分配和释放的内存区域。在函数调用时,局部变量和函数参数会在栈上分配内存,当函数结束时,栈上的内存自动释放。一般函数在栈上。
通过指针,我们可以访问堆上分配的内存。
在 C++ 中,你可以使用 new
操作符来在堆上动态申请内存。下面是一个示例,展示如何申请一个 int
类型的指针 p
并将其存放在堆上:
int* p = new int; // 在堆上申请一个 int 类型的内存空间,并将其地址赋给指针 p
这个代码完成了两件事:
- 使用
new
关键字在堆上分配了足够的内存空间来存储一个int
类型的值。 - 将分配到的内存地址赋给指针
p
。
注意,当你使用 new
在堆上分配内存时,你需要负责后续的内存管理。这意味着,当你不再需要这块内存时,你应该使用 delete
操作符来释放它,以避免内存泄漏(程序持续运行,不释放的内存会逐渐累积,最终可能导致程序或整个系统可用内存不足。):
delete p; // 释放之前分配给 p 的内存
如果你申请了一个数组,应该使用 delete[]
来释放内存:
int* pArray = new int[10]; // 在堆上申请一个 int 类型数组的内存空间
// ...
delete[] pArray; // 释放数组占用的内存
想象你在一个图书馆里借了一本书,
new int
就像是你向图书馆管理员请求一个空书架,管理员会给你一个编号,告诉你这个书架现在归你使用。这个编号,就是我们的指针p
。你有了一个地方来存放你的书,也就是你的int
类型的数据。new
这个操作,就像是你告诉管理员:“我要一个新书架,不要旧的。”当你读完书后,你决定将书归还给图书馆,让他们可以回收这个空间,给其他需要的人使用。这相当于在编程中使用delete p;
来释放指针p
指向的内存。如果你归还了书,却没有让图书馆工作人员知道这个书架现在空了(
p
在释放内存后未设置为nullptr
),会发生什么呢?再次使用这个编号去查找书,你可能会空手而归。(指针指向的内存已经被释放,但指针仍然指向那个位置,导致无法访问有效的数据)
错误地认为书架上还有书(数据),尝试再次申请借阅(尝试访问已经被释放的内存,可能会导致程序错误或崩溃)
其他读者可能会用这个编号去错误地取书(数据),或者尝试在那个书架(空间)上放置自己的书(安全漏洞,可能导致数据覆盖或未授权的访问)
C++内存管理:理解堆、栈、指针,避免内存泄漏-腾讯云开发者社区-腾讯云 (tencent.com)
动态库与静态库区别
用一个比喻来描述动态库和静态库的区别:
动态库
想象一辆公共汽车,任何人都可以在任何时间上车(加载库),并在需要的时候下车(卸载库)。车上的座位(内存)是共享的,不同的乘客(程序)可以使用相同的座位,但不需要拥有它。如果公共汽车需要维修或升级,只需更换车辆,所有乘客都能享受到改进,无需额外费用。
静态库
想象一辆私家车,当你购买它时,它包含了所有你需要的部件(代码)。每次出行(运行程序),你只能使用这辆车和它的部件。如果你想更换部件或升级,你需要自己掏钱购买新的部件并安装。每次出行,无论是否需要,你都必须携带所有部件,即使有些部件在某些行程中并未使用。
如何把一个函数导出一个动态库
见c++相关函数和类的dll导入及导出(VScode)-CSDN博客
面向对象的多态性
多态:当不同的对象去完成某一个行为时会产生出不同的状态。 “一个接口,多种方法”
或者说,不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。
函数继承中构成多态的条件:
- 通过基类的指针或者引用调用虚函数。
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写(覆盖)
【C++】多态(举例+详解,超级详细)_c++多态-CSDN博客
虚函数
虚函数是实现多态(动态绑定)/接口函数的基础
可以定义一个基类的指针, 其指向一个继承类, 当通过基类的指针去调用函数时, 可以在运行时决定该调用基类的函数还是继承类的函数
接口继承
以【C++】多态(举例+详解,超级详细)_c++多态-CSDN博客其中一个代码为例:
函数利用父对象指针来访问子对象,然后根据子对象的函数来调用。虚函数的重写是接口继承,实现重写
两个类
A
和B
,其中B
继承自A
。基类A
中有虚函数func
和 虚函数test(
test调用了func)
,而派生类B
重写了func
函数。注意,B
中的func
函数重写了类A
中的func
,它们具有不同的默认参数值。(这里重写的定义我查了,默认参数不同不构成重载)主函数中创建了B
类的一个实例,并通过基类指针p
调用了test。
由于
test
函数在A
中定义,调用的是A
中的test
函数,,test
函数内部调用的是func
函数。而p 实际上指向 B 类的对象,又由于func
是虚函数,重写使得在运行p->test()
时,实际调用的是B
中的func
函数的B->但由于默认参数在编译时确定(而非运行时),因此使用的是
A
中定义的默认参数1
。
补充:默认参数的行为
在C++中,默认参数是在编译时解析的。当通过基类指针或引用调用虚函数时,默认参数值是根据基类中的定义来确定的,而不是根据实际调用的派生类中的。
代码运行结果是B->1。
STL标准模板库——数据类型的多态
在C++中,标准模板库(STL)通过模板机制体现了在数据类型上的多态性。STL中的容器、算法和迭代器都是通过模板来实现的,这使得它们可以处理不同类型的数据,不需要为每种数据类型单独实现一套代码。
模板机制
C++的模板机制是实现多态性的基础。模板允许你定义一个通用的类或函数,而不需要指定具体的类型。模板参数可以在实例化时被具体的类型替换。
模板类
模板类允许你定义一个通用的类,其中的某些成员可以是任意类型。例如,std::vector
是一个模板类,可以存储任意类型的元素。
template <typename T>
class vector {
// 类的实现
};
使用时可指定具体类型:
std::vector<int> intVector; // 存储int类型的元素
std::vector<std::string> stringVector; // 存储std::string类型的元素
模板函数
模板函数允许你定义一个通用的函数,其中的某些参数可以是任意类型。例如,std::sort
是一个模板函数,可以对任意类型的容器进行排序。
template <typename RandomIt>
void sort(RandomIt first, RandomIt last) {
// 函数的实现
}
容器
STL中的容器类(如vector
、list
、map
等)都是模板类。例如,std::vector<T>
可以存储任意类型的元素T
,无论是基本数据类型(如int
、double
)还是用户定义的类型(如自定义的类)。
std::vector<int> intVector = {1, 2, 3, 4, 5};
std::list<double> doubleList = {1.1, 2.2, 3.3};
std::map<std::string, int> stringToIntMap = {{"one", 1}, {"two", 2}};
算法
STL中的算法(如std::sort
、std::find
、std::transform
等)也是通过模板实现的。这些算法可以应用于不同类型的容器和迭代器。
std::vector<int> numbers = {3, 1, 4, 1, 5, 9};
std::sort(numbers.begin(), numbers.end()); // 对int类型的vector进行排序
std::vector<std::string> words = {"apple", "orange", "banana"};
std::sort(words.begin(), words.end()); // 对std::string类型的vector进行排序
迭代器
迭代器是STL中连接容器和算法的桥梁。迭代器也是模板化的,可以指向不同类型的容器和元素。
std::vector<int>::iterator it = numbers.begin(); // 指向int类型vector的迭代器
std::vector<std::string>::iterator wordIt = words.begin(); // 指向std::string类型vector的迭代器