gcc和g++编译器的区别:
- gcc是GCC中的GUN C Compiler(C 编译器)
- g++是GCC中的GUN C++ Compiler(C++编译器)
包含sleep的包,在windows下是windows.h,在linux下是unistd.h
cmake根据cmakefilelist生成MakeFile文件,make再执行MakeFile中的命令
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据。
volatile int i=10;
int a = i;
int b = i;
volatile 指出 i 是随时可能发生变化的,每次使用它的时候必须从 i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在 b 中。而优化做法是,由于编译器发现两次从 i读数据的代码之间的代码没有对 i 进行过操作,它会自动把上次读的数据放在 b 中。而不是重新从 i 里面读。
可以为 std::thread
对象指定一个回调函数,该回调函数会在新线程启动时被执行。回调函数的形式可以是:
- 函数指针
- 函数对象
- lambda 表达式
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
其中,timespec和timeval都在time.h包中,重复定义会报错。CLOCK_MONOTONIC这个参数的clock_gettime获取到的时间是Linux系统启动以来的秒数和纳秒数,并存储在结构体timespec中
虚函数
虚函数的作用是由静态链接变为动态链接,实际来说,就是在用父类指针调用函数时,实际调用的是指针指向的实际类型(子类)的成员函数。
我们将指针b做强制类型转换,转换为A*类型,然后分别调用func和vir_func函数,发现普通函数调用的是类A的函数,而虚函数调用的是类B的函数。当使用类的指针调用成员函数时,普通函数由指针类型决定,而虚函数由指针指向的实际类型决定
虚函数是由虚函数指针实现的,当类中存在虚函数时,类的占用内存会多出4个字节,而多出来的4个字节就是虚函数指针所占用的。(注:sizeof的值只包括虚函数指针和对象变量的占用内存,存放在堆中,类成员函数都存储在代码区)虚函数指针指向一个虚函数表,中会有多个函数指针,分别指向对应的虚函数实现区域。通过对象内存中的虚函数指针找到虚函数表,接着通过虚函数表中的函数指针找到对应虚函数的实现区域并调用。
构造函数不能是虚函数,析构函数可以是虚函数且推荐最好设置为虚函数。因为构造对象的时候都是由类名构造,不可能出现基类类型实际指向子类的情况;而析构的时候则是由对象名析构,这时候希望调用对象实际指向的类的析构函数。
静态(static)
静态变量:声明为static的变量只被初始化一次,因为它们在单独的静态存储中分配了空间,因此类中的静态变量由对象共享,可以减小对象空间占用的大小。对于不同的对象,不能有相同静态变量的多个副本。也是因为这个原因,静态变量不能使用构造函数初始化。类中的静态成员变量必须在类内声明,在类外定义(被const修饰的除外)
静态函数:建议使用类名和范围解析运算符调用静态成员。静态成员函数仅能访问静态数据成员或其他静态成员函数,它们无法访问类的非静态数据成员或成员函数。
静态类:和变量一样,静态类的生命周期直到程序的结束。在main结束后才会调用静态类的析构函数。
单例模式(Singleton)
保证每一个类仅有一个实例,并为它提供一个全局访问点。有些时候,某些组件在整个程序运行时就只需要一个对象,多余的对象反而会导致程序的错误。或者,有些属性型对象也只需要全局存在一个。比如,假设黑体字属性是一种对象,调用黑体字属性对象的函数来让一个字变成黑体。显然并不需要在每创造一个黑体字时就生成一种属性对象,只需要调用当前存在的唯一对象的函数即可。
单例模型的构造函数中有一个固定用法:
GetInstance(Args&&...args)
object = new T(std::forward<Args>(args)...)
cpp中注意单引号和双引号之间的区别
cpp中string使用replace会直接改变原始对象,甚至在replace之前赋值一个新的string,这个新的string也会被改变,而python中似乎只创建一个临时值,注意区别
(实际上,string的复制如果修改了就是深拷贝,如果没有修改就是浅拷贝。具体的实现方法,是对数据块进行了引用计数,尝试修改的时候,引用计数不为1,就要复制再修改。)
不要在同一个string相加表达式中使用两次或者多次replace,如果要写,要以该string没有改变为准则来使用第二个以及后面的replace函数
指针:
int a=8;
int *ptr = &a;
printf("%d\n", *ptr); //打印a的值8
printf("%p\n", ptr); //打印a的地址
struct A{
int length;
char *a_char;
}
A a = {8, (char*)"abc"};
A *ptr_a = &a;
A **pptr_a = &ptr_a;
printf("%d\n", ptr_a->length) //打印A结构体中length值8
printf("%d\n", (*ptr_a).length) //打印A结构体中length值8
printf("%d\n", (**pptr_a).length) //打印A结构体中length值8
printf("%d\n", (*pptr_a)[0].length) //打印A结构体中length值8,注意指针的 [] 用法
其中,结构体A的指针ptr_a的内存为8个字节,增加结构体的元素,该值似乎也不变
如果打印ptr_a的值可以发现,它由12个16进制数组成,共48位,而不是64位,根据网上所说,逻辑地址只使用低48位;并且可以发现,新构建对象的地址末尾总是0,这是因为对齐导致的。
类中的对构造函数和赋值函数定义为default和delete
如果对构造函数进行了重载,则编译器不会隐式的生成一个默认的构造函数,此时如果调用了默认构造函数会在编译时报错,为了避免这种情况,一般会选择重写默认构造函数,且函数体为空。关键字 =default 优化了这种行为,用该关键字标记重写的默认拷贝构造函数,编译器会隐式生成一个版本,在代码更加简洁的同时,编译器隐式生成的版本的执行效率更高
class M{
M()=default; //标记隐式生成该类的默认构造函数
}
拷贝构造函数 和 拷贝赋值函数:不论有没有对它们进行重载,编译器始终会隐式生成默认版本,但有的时候不希望类实例进行拷贝构造或拷贝赋值,此时可以重写它们并将权限设置为private,但这样只是利用语法特性来碰巧实现效果,且对友元会带来困惑。可以使用关键字 =delete 标记不想被类实例调用的拷贝构造和拷贝赋值,相当于删除了它们
M()=delete;
M(M& m)=delete;
M& operator=(M& ob)=delete
析构函数:一个类中有且仅有一个析构函数(所以=delete不能用于析构函数),它是一个无参无返回值的特殊成员函数,由系统自动调用,析构函数如果被重写,则系统会调用重写的版本,否则调用隐式生成的默认版本
静态变量与全局变量的区别是?
静态变量和全局变量的作用域和生命周期不同,静态变量仅仅作用于定义它的函数或者文件内(静态局部和静态全局),而全局变量可以作用于任何位置;全局变量的生命周期与程序的生命周期相同,而静态变量的生命周期与定义它的函数或者文件相同
动态库和静态库的区别?
动态库是在运行时加载到内存中,可以被多个程序共享使用,因此它的体积比较小。 而静态库则是在编译时直接链接进可执行文件中,增加了可执行文件的体积。 但是静态库不需要在运行时再加载,因此程序的运行速度会相对较快。
编译
在工程中引用opencv库相关api,首先需要安装opencv,具体参考知乎的帖子
默认安装路径:/usr/local
需要将/usr/local/include下的opencv2文件夹复制到工程文件夹,里面包含了opencv的头文件,并且在代码中要加入#include <opencv2/opencv.hpp>
其次在编译的时候,需要在编译命令的后面加上:
-l opencv_flann $(LIBS)
例如:
LIBS += $(LIB_DIR)/libopencv_imgproc.so \
$(LIB_DIR)/libopencv_imgcodecs.so \
$(LIB_DIR)/libopencv_core.so \
$(LIB_DIR)/libopencv_calib3d.so \
$(LIB_DIR)/libopencv_videoio.so
clang++ $(CXXFLAGS) $(LINKFLAGS) -shared $^ -o $@ -l opencv_flann $(LIBS)
opencv
opencv引用只需要#include <opencv2/opencv.hpp>
opencv中的Mat类构造函数的前两个参数为Height和Width,也就是依次为高和宽
而opencv中的Rect类构造函数的前四个参数为:左上角x坐标,左上角y坐标,Width,Height
如果用Rect来实现裁剪,要注意两个类的构造参数是相反的
new (std::nothrow) Logger(callback, maxLevel, status)
使用 new
操作符在堆上动态分配一个新的 Logger
对象。std::nothrow
参数确保如果分配失败(即无法分配内存),new
返回一个空指针 (nullptr
)。这是一种处理内存分配失败的安全措施,能够优雅地处理异常情况。
这是对 Logger
类构造函数的调用。它使用参数 callback
、maxLevel
和 status
初始化一个新的 Logger
对象。
gdb调试相关
全网最全GDB调试整理 - 小白编程开发 - 博客园 (cnblogs.com)
【GDB】手把手教你用gdb调试程序(超清晰流程)_gdb教程-CSDN博客
使用GDB(一):分析core.xxx文件常用方法_gdb core文件怎么分析-CSDN博客
使用GDB(二):调试程序常用命令_gdb 执行命令文件-CSDN博客
【Qnx】Qnx coredump解析_qnx的core dump怎么查看-CSDN博客
qnx编译器编译的程序需要用qnx里面的gnu debugger来调试,和Linux下的gdb不能通用
cuda
两个1<<14长度的向量相加,采用一维块,二维网格,每个块32个线程,计算耗时0.013ms;而同样的配置下,每个线程计算两个元素的相加,总耗时是0.012ms
代码可写为:
unsigned int ix = (blockidx.x * blockdim.x + threadidx.x) * 2
unsigned int iy = blockidx.y
unsigned int idx_1 = iy * nx + ix
unsigned int idx_2 = iy * nx + ix + 1
if(ix<nx && iy<ny)
res[idx_1] = a[idx_1] + idx[idx_1]
res[idx_2] = a[idx_2] + idx[idx_2]