1 C++ 基础知识
1.1 头文件的作用?
答:提供全局变量、全局函数的声明或公用数据类型的定义,从而实现分离编译和代码复用。
1.2 在 main 执行之前和之后执行的代码可能是什么?
答:main 函数执行之前,主要就是初始化系统相关资源:
- 设置栈指针。
- 初始化静态 static 变量和 global 全局变量,即 .data 段的内容。
- 将未初始化部分的全局变量赋初值:数值型 short,int,long 等为 0,bool为 FALSE ,指针为 NULL 等等,即 .bss 段的内容。
- 全局对象初始化,在 main 之前调用构造函数,这是可能会执行前的一些代码。
- 将main函数的参数 argc,argv 等传递给 main 函数,然后才真正运行 main 函数。
- __attribute__((constructor))。
main 函数执行之后:
- 全局对象的析构函数会在 main 函数之后执行。
- 可以用 atexit 注册一个函数,它会在 main 之后执行。
- __attribute__((destructor))。
1.3 结构体内存对齐问题?
答:结构体内成员按照声明顺序存储,第一个成员地址和整个结构体地址相同。
未特殊说明时,按结构体中 size 最大的成员对齐(若有 double 成员,按8字节对齐。)
c++11 以后引入两个关键字 alignas 与 alignof。其中 alignof 可以计算出类型的对齐方式,alignas 可以指定结构体的对齐方式。
1.4 指针和引用的区别?
答:
- 指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名。
- 指针可以有多级,引用只有一级。
- 指针可以为空,引用不能为 NULL 且在定义时必须初始化。
- 指针在初始化后可以改变指向,而引用在初始化之后不可再改变。
- sizeof 指针得到的是本指针的大小,sizeof 引用得到的是引用所指向变量的大小。
- 当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,两者指向的地址相同,但不是同一个变量,在函数中改变这个变量的指向不影响实参,而引用却可以。
- 引用本质是一个指针,同样会占4字节内存;指针是具体变量,需要占用存储空间(,具体情况还要具体分析)。
- 引用在声明时必须初始化为另一变量,一旦出现必须为 typename refname &varname 形式;指针声明和定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量。
- 引用一旦初始化之后就不可以再改变(变量可以被引用为多次,但引用只能作为一个变量引用);指针变量可以重新指向别的变量。
- 不存在指向空值的引用,必须有具体实体;但是存在指向空值的指针。
1.5 在传递函数参数时,什么时候该使用指针,什么时候该使用引用呢?
答:
- 需要返回函数内局部变量的内存的时候用指针。使用指针传参需要开辟内存,用完要记得释放指针,不然会内存泄漏。而返回局部变量的引用是没有意义的。
- 对栈空间大小比较敏感(比如递归)的时候使用引用。使用引用传递不需要创建临时变量,开销要更小。
- 类对象作为参数传递的时候使用引用,这是 C++ 类对象传递的标准方式。
1.6 extern 的作用?
答:(1)声明全局变量。
(2)C++ 代码中调用 C 语言代码。
1.7 C++ 中四种类型转换方式?
答:
1.8 访问权限
2 C++ 扩展知识
2.1 C++ 和 Python 混合编程如何实现?
答: ctypes。
swig:swig是另外一种把 C/C++ 代码接口给 python 或其他语言调用的工具,SWIG 本质上是个代码生成器,为 C/C++ 程序生成到其他语言的包装代码(wrapper code),这些包装代码里会利用各语言提供的 C API,将 C/C++ 程序中的内容暴露给相应语言。为了生成这些包装代码,SWIG 需要一个接口描述文件,描述将什么样的接口暴露给其他语言。SWIG 的接口描述文件可以包含以下内容:1)ANSI C 函数原型声明;2)ANSI C 变量声明;3) SWIG指示器(directive)相关内容。SWIG 可以直接接受 ”.h” 头文件做为接口描述文件。在有了接口描述文件后,就可以利用 swig 命令生成包装代码了,然后将包装代码编译链接成可被其他语言调用的库。
2.2 初始化与赋值的区别?
答:初始化需要先分配空间。
赋值是对已有对象的操作。
3 Qt
3.1 QString 和 string 的区别?
答:QString 提供了处理字符的函数。
size()——字符串大小。(一个汉字占两个字符。)
append()——字符串拼接。
toUpper() 和 toLower()——字符串大小写。
mid(start, number)——字符串截取。
trimmed()——去掉字符串首尾的空格。
simplified()——不仅去掉首尾的空格,中间连续的空格用一个空格替换。
replace(a, b)——将字符 a 替换成 b。
indexOf()——查询第一个出现的位置。
lastIndexOf()——查询最后一个出现的位置。
toStdString()——QString 转 std::string。
fromStdString()——std::string 转 QString。
contains()——判断字符串中是否包含某个字符。
isNull() 和 isEmpty()——如果一个空字符串,只有“\0”,isNull() 返回 false,而 isEmpty() 返回 true。
3.2 QT下如何使用多线程?
答:方式一:
(1)创建一个线程类的对象,继承 QThread。
class MyThrad:public QThread
{
......
}
(2)重写父类的 run()
方法,在该函数内部编写子线程要处理的具体业务流程。
class MyThread:public QThread
{
protected:
void run()
{
.........;
}
}
(3)在主线程中创建子线程对象,new 一个。
MyThread * subThread = new MyThrad;
(4)启动子线程,调用 start()
方法。
subThread->start();
不能在类的外部调用 run() 方法启动子对象,在外部使用 start() 相当于 run()。
创建出子线程后,父子线程之间的通信可以通过信号槽的方式。
Qt 子线程对象不能操作窗口类型对象,只有主线程才能操作窗口对象。
(5)释放线程资源,使用信号槽机制,窗口关闭时释放。
connect(this,&QWidget::destroyed,this,[=](){
//t1 为创建的子线程对象
gen->quit();
gen->wait();
gen->deleteLater();
});
3.3 QT 信号槽的5种连接方式?
答:(1)AutoConnection——如果接收者和发送者在同一个线程,则自动使用 DirectConnection 类型。
如果接收者和发送者不在同一个线程,则自动使用 QueuedConnection 类型。
(2)DirectConnection——槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。
效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
(3)QueuedConnection——槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。
发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
(4)BlockingQueuedConnection——槽函数的调用时机与 QueuedConnection 一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。
使用该连接方式,接收者和发送者绝对不能在一个线程,否则程序会死锁!!!在多线程间需要同步的场合可能需要这个。
(5)UniqueConnection——这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。