面试题目记录-创新互联
- C++
- 多线程
- 拷贝构造、移动构造、赋值运算符、移动赋值运算符
- 返回值
- i++/++i
- 操作符重载
- emplace
- 右值引用
- 智能指针
- Lambda表达式
- Lambda使用问题
- 智能指针的实现
- 编译和内存知识
- QT
- 使用qml有哪些问题
- win32窗口从点击到相应的过程
- 共享内存
- 共享内存上创建C++对象的问题
推荐几款实用的C++ 在线工具
C++、Qt基础面试题定制网站设计可以根据自己的需求进行定制,成都网站制作、网站建设构思过程中功能建设理应排到主要部位公司成都网站制作、网站建设的运用实际效果公司网站制作网站建立与制做的实际意义C++ 多线程
使用多线程及其同步的方法,写出一个多线程打印0,1,0,1序列的功能。或者写出一个生产者消费者的功能。
条件变量的wait为何有第二个参数?
- 拷贝构造、赋值运算符:注意深拷贝资源的指针。
通常在类中自定义移动构造函数的同时,会再为其自定义一个适当的拷贝构造函数,由此当用户利用右值初始化类对象时,会调用移动构造函数;使用左值(非右值)初始化类对象时,会调用拷贝构造函数。
Test拥有默认构造函数、析构函数、拷贝构造、移动构造、移动赋值、拷贝赋值运算符。看fun函数对Test这些函数的调用时机。
Test fun() {
Test t;
return t;
}
有移动构造和没有移动构造,这里的不一样:
- 没有移动构造:Test默认构造 -》用t去构造一个临时变量,Test的拷贝构造 -》t变量析构,析构函数 -》返回临时变量
- 有移动构造:Test默认构造 -》用t去构造一个临时变量,Test的移动构造 -》t变量析构,析构函数 -》返回临时变量
class Demo {
public:
Demo() : num(new int(0)) {
cout<< "construct!"<< endl;
}
Demo(const Demo &d) : num(new int(*d.num)) {
cout<< "copy construct!"<< endl;
}
Demo(Demo &&d) : num(d.num) {
d.num = nullptr;
cout<< "move construct!"<< endl;
}
~Demo() {
cout<< "class destruct!"<< endl;
}
private:
int *num;
};
Demo getDemo() {
Demo a;
return a;
}
int main()
{
auto b = getDemo();
return 0;
}
i++/++iint i = 1;
int a = i++;
i = 1;
int b = ++i;
i = 1;
int c = ++i + ++i;
a = 1 b = 2 c = 6
操作符重载参考:C++操作符重载
emplace- 解释
vector.push_back("xyzzy");
的过程
1、"xyzzy"调用string构造函数,得到一个临时右值变量temp
2、调用push_back的右值重载函数,把temp实参传入给其形参x,然后内部调用vector的移动构造函数,得到一个副本,插入到vector容器中。
3、temp临时变量析构
而采用emplace_back则没有第一步,只有第二步,那么当然的第三步也没有。
参考:https://cntransgroup.github.io/EffectiveModernCppChinese/8.Tweaks/item42.html
- std::move的实现
template//在std命名空间
typename remove_reference::type&&
move(T&& param)
{
using ReturnType = //别名声明,见条款9
typename remove_reference::type&&;
return static_cast(param);
}
- 解释下面的代码
value(std::move(text))
是否调用了string的移动构造函数?
class Annotation {
public:
explicit Annotation(const std::string text)
:value(std::move(text))
{ … }
…
private:
std::string value;
};
参考:https://cntransgroup.github.io/EffectiveModernCppChinese/5.RRefMovSemPerfForw/item23.html
记住:
第一,不要在你希望能移动对象的时候,声明他们为const。对const对象的移动请求会悄无声息的被转化为拷贝操作。第二点,std::move不仅不移动任何东西,而且它也不保证它执行转换的对象可以被移动。
- std::forward是怎么知道它的实参是否是被一个右值初始化的?
std::forward只对实参是右值时才做转换,把形参转为右值(所有的函数参数,都是左值,当实参是左值时,没必要转换)。
- shared_ptr是否线程安全?
多线程操作同一个shared_ptr对象时,不是线程安全的。多线程操作多个shared_ptr对象(指向一块内存地址),是线程安全的,不需要去管。踏马的,这个不是正常吗,都不是一个对象了,多线程当然是安全的。
另一个层面就是:shared_ptr对象所管理的那块内存是不是线程安全的。答,不是。多个shared_ptr对象,在多线程中,共同所指的那块内存地址的操作是不安全的。但也不一定都无脑的加锁去判断。有一种解决方式就是,用一个原子变量代表是否要对这块内存做处理,而在回到顺序处理的流程中,通过判断这个原子变量是否是true,来做最终的处理。
另一个层面就是:shared_ptr初始化时的线程安全性。make_shared是线程安全的。
processWidget(std::shared_ptr(new Widget), //潜在的资源泄漏!
computePriority());
// 解决方法1
processWidget(std::make_shared(), //没有潜在的资源泄漏
computePriority());
// 解决方法2
std::shared_ptrspw(new Widget);
processWidget(spw, computePriority()); // 正确,但是没优化,因为这里spw是一个左值,方法1中是一个右值参数
// 解决方法3
processWidget(std::move(spw), computePriority()); //高效且异常安全
因为std::shared_ptr
是两个步骤:1、new Widget
2、shared_ptr
的构造函数,而std::make_shared
是一步操作。参考:条款二十一:优先考虑使用std::make_unique和std::make_shared,而非直接使用new
参考:https://blog.csdn.net/bureau123/article/details/121300979
https://zhuanlan.zhihu.com/p/416289479
- make_shared
// 两次分配内存,一次new Widget,一次shared_ptr构造函数中包含引用计数的控制块
std::shared_ptrspw(new Widget);
// 一次分配,分配的内存同时容纳了Widget对象和控制块
auto spw = std::make_shared();
- make函数的缺点
make函数都不允许指定自定义删除器。
和直接使用new相比,make函数消除了代码重复,提高了异常安全性。对于std::make_shared和std::allocate_shared,生成的代码更小更快。
不适合使用make函数的情况包括需要指定自定义删除器和希望用花括号初始化。
对于std::shared_ptrs,其他不建议使用make函数的情况包括(1)有自定义内存管理的类;(2)特别关注内存的系统,非常大的对象,以及std::weak_ptrs比对应的std::shared_ptrs活得更久。
Lambda实现原理:参考https://www.zhihu.com/question/57241113/answer/2440288161
Lambda捕获原理,在定义时捕获还是运行时捕获? 运行时,如果是定义时,那么某个捕获的变量如果变化了,那这个变化的值就得不到了。
下面这段代码有什么问题:
int i = 10;
auto f = [=]() {
i = 9;
};
Lambda使用问题- 引用捕获外部的局部变量,此时可能在执行Lambda表达式时,该捕获的局部变量早已脱离作用域而析构了,导致悬空引用。此时需要改用值捕获来解决。
- 值捕获一个指针变量。需要注意这个指针变量所指的内存是否已经被外面释放了。此时最好值捕获一个shared_ptr,或者使用深拷贝(即重写赋值运算符,里进行深拷贝)。
- 捕获this问题。如果使用默认捕获,或者值捕获类对象的成员变量,都会存在潜在的问题。即类对象可能会析构了,而lambda表达式中的对成员变量的使用还在继续,这会导致崩溃。
解决办法:1、捕获一个对这个成员变量的临时赋值的变量,而不是这个成员变量本身。2、使用C++14的广义Lambda 3、C++17增加了新特性可以捕获*this,不持有this指针,而是持有对象的拷贝,这样生命周期就与对象的生命周期不相关,使用上就安全一些。
参考:Effective Modern C++ 条款31
参考:c++的lambda使用注意事项,可能导致的崩溃问题分析
// 定义一个引用计数类,封装接口
class SharedCount
{
public:
SharedCount() : m_count(1) {}
void Add() { m_count++; }
auto Reduce() { return --m_count; }
auto GetCount() { return m_count; }
private:
unsigned long m_count;
};
templateclass SharedPtr
{
public:
// 默认构造
SharedPtr() : m_ptr(nullptr), m_count(nullptr) {}
// 普通指针初始化
SharedPtr(T* t = nullptr) : m_ptr(t) {
if (t != nullptr) {
m_count = new SharedCount;
}
}
~SharedPtr() {
if (m_ptr && m_count->Reduce() == 0) {
delete m_ptr;
m_ptr = nullptr;
delete m_count;
m_count = nullptr;
}
}
// 拷贝构造
SharedPtr(const SharedPtr&other) :
m_count(other.m_count), m_ptr(other.m_ptr) {
if (other.m_count) {
other.m_count->Add();
}
}
// 移动构造
SharedPtr(SharedPtr&&other)
// : m_count(std::move(other.m_count)),
// m_ptr(std::move(other.m_ptr))
{
this->m_count = other.m_count;
this->m_ptr = other.m_ptr;
other.m_ptr = nullptr;
}
// 赋值运算
SharedPtr& operator= (const SharedPtr& other) {
if (m_ptr == other.m_ptr) {
return *this;
}
// 目的对象不空
if (m_ptr != nullptr) {
if (m_count->Reduce() == 0) {
delete m_ptr;
}
}
// 赋值
this->m_ptr = other.m_ptr;
other.m_count->Add();
this->m_count = other.m_count;
return *this;
}
// 移动赋值运算符
SharedPtr& operator=(const SharedPtr&&other) {
if (m_ptr == other.m_ptr) {
return *this;
}
if (m_ptr != nullptr) {
if (m_count->Reduce() == 0) {
delete m_ptr;
}
}
m_ptr = other.m_ptr;
m_count = other.m_count;
other.m_ptr = nullptr;
return *this;
}
// 解引用
T* operator->() {
return m_ptr;
}
T& operator* () {
return *m_ptr;
}
SharedPtr& swap(SharedPtr&other) {
}
auto Get() { return m_ptr; }
auto GetCount() { return m_count ? m_count->GetCount() : 0; }
private:
T* m_ptr = nullptr;
SharedCount* m_count = nullptr;
};
编译和内存知识这部分需要完整看完【程序员的自我修养】
QT 使用qml有哪些问题- 避免定时轮询,采用信号槽机制通知。
- 避免频繁的属性访问,如果必要,可以先用一个变量存储这个属性,再去频繁范围这个变量即可。尽量用临时变量替代对属性的访问。
- 属性绑定时,避免复杂的计算。简单的计算,QML可以直接得出结果,效率比较快。
- 尽量少用属性绑定(因为被绑定的值一旦变化,属性表达式将重新计算)。用锚布局。
- 属性设置为异步加载,asynchronous异步属性设置为true,在组件实例化时可以提高流畅性。
- 避免var的声明,声明为具体的类型,局部变量声明未let
https://www.jianshu.com/p/e6fcb575f916
参考:https://leetcode.cn/circle/discuss/g4YxE2/
共享内存 共享内存上创建C++对象的问题参考:https://blog.csdn.net/dickyjyang/article/details/21403451
https://www.cnblogs.com/yangru/p/3805192.html
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
本文标题:面试题目记录-创新互联
标题路径:http://ybzwz.com/article/ispod.html