C++常见笔试面试要点以及常见问题
https://www.cnblogs.com/jiayayao/p/6246468.html
1. C++常见笔试面试要点:
C++语言相关:
(1) 虚函数(多态)的内部实现(C++虚函数实现多态原理(转载))
(2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现原理是什么? (智能指针shared_ptr的用法、智能指针unique_ptr的用法)
(3) 特化和泛化(算法的泛化过程(摘自《STL源码剖析》)
(4) 为什么不能在构造函数中调用虚函数?
当实例化一个派生类对象时,首先进行基类部分的构造,然后再进行派生类部分的构造。当在构造基类部分时,派生类还没被完全创建,从某种意义上讲此时它只是个基类对象。所以在构造函数中调用虚函数,执行的是基类的虚函数,而非派生类的虚函数。
STL:
(1) vector、list、set、map内部实现以及异同,迭代器插入删除后vector、list、set、map的迭代器是否会失效?
(2) STL除了序列式容器和关联式容器,还有哪些值得学习的?
TCP/IP:
(1) TCP、UDP异同;
(2) TCP、UDP发送一段字符串,其中的发送过程有什么区别?本人理解:TCP有滑动窗口,流量控制,超时重传等机制。
数据结构:
(1) 链表的逆置(又称反转);
(2) 链表有无环的检测,两个链表相交有无可能?相交的形态是什么样的?如何确定相交点?
(3) 如何求出数组中最小(或者最大)的k个数(least k问题)?
(4) 如何求出树中两个结点的最低公共祖先?
本人理解:以上要点必须从根本上明白,理解,不能有糊涂的地方,因为面试官往往会由浅入深的提问,当你不明白一点后,面试官就无需再往下问了,直接确定你的水平了。
智能指针unique_ptr的用法
2016-12-03 17:19 by jiayayao, 2048 阅读, 0 评论, 收藏, 编辑
unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法:
std::unique_ptr<T> myPtr(new T);
std::unique_ptr<T> myOtherPtr = myPtr; // error
但是unique_ptr允许通过函数返回给其他的unique_ptr,还可以通过std::move来转移到其他的unique_ptr,注意,这时它本身就不再拥有原来指针的所有权了。相比于auto_ptr而言,unique_ptr是显示的转移,而不是莫名其妙的报废,因为auto_ptr调用拷贝构造函数后,原来的对象就失效了。
std::unique_ptr<T> myPtr(new T);
std::unique_ptr<T> myOtherPtr = std::move(myPtr); // ok
对于一般的程序使用std::unique_ptr就够了,如果是多线程方面,可能存在共同使用的问题,可以使用std::shared_ptr,注意不要引起循环引用。
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer)。
智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈上的变量自动被销毁,智能指针内部保存的内存也就被释放掉了(除非将智能指针保存起来)。
C++11提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件<memory>。
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。
- shared_ptr的基本用法
- 初始化
可以通过构造函数、std::make_shared<T>辅助函数和reset方法来初始化shared_ptr:

#include "stdafx.h"
#include <iostream>
#include <future>
#include <thread>
using namespace std;
class Person
{
public:
Person(int v) {
value = v;
std::cout << "Cons" <<value<< std::endl;
}
~Person() {
std::cout << "Des" <<value<< std::endl;
}
int value;
};
int main()
{
std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用计数为1
std::shared_ptr<Person> p2 = std::make_shared<Person>(2);
p1.reset(new Person(3));// 首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
// 最后将新对象的指针交给智能指针
std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引用计数为2
p1.reset();//Person(3)的引用计数为1
p3.reset();//Person(3)的引用计数为0,析构Person(3)
return 0;
}

注意,不能将一个原始指针直接赋值给一个智能指针,如下所示,原因是一个是类,一个是指针。
std::shared_ptr<int> p4 = new int(1);// error
reset()包含两个操作。当智能指针中有值的时候,调用reset()会使引用计数减1.当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。
- 获取原始指针
std::shared_ptr<int> p4(new int(5));
int *pInt = p4.get();
- 指定删除器
智能指针可以指定删除器,当智能指针的引用计数为0时,自动调用指定的删除器来释放内存。std::shared_ptr可以指定删除器的一个原因是其默认删除器不支持数组对象,这一点需要注意。
2. 使用shared_ptr需要注意的问题
但凡一些高级的用法,使用时都有不少陷阱。
- 不要用一个原始指针初始化多个shared_ptr,原因在于,会造成二次销毁,如下所示:
int *p5 = new int; std::shared_ptr<int> p6(p5); std::shared_ptr<int> p7(p5);// logic error - 不要在函数实参中创建shared_ptr。因为C++的函数参数的计算顺序在不同的编译器下是不同的。正确的做法是先创建好,然后再传入。
function(shared_ptr<int>(new int), g());
- 禁止通过shared_from_this()返回this指针,这样做可能也会造成二次析构。
- 避免循环引用。智能指针最大的一个陷阱是循环引用,循环引用会导致内存泄漏。解决方法是AStruct或BStruct改为weak_ptr。

struct AStruct;
struct BStruct;
struct AStruct {
std::shared_ptr<BStruct> bPtr;
~AStruct() { cout << "AStruct is deleted!"<<endl; }
};
struct BStruct {
std::shared_ptr<AStruct> APtr;
~BStruct() { cout << "BStruct is deleted!" << endl; }
};
void TestLoopReference()
{
std::shared_ptr<AStruct> ap(new AStruct);
std::shared_ptr<BStruct> bp(new BStruct);
ap->bPtr = bp;
bp->APtr = ap;
}
C++11之std::function和std::bind
2016-12-06 21:30 by jiayayao, 20016 阅读, 0 评论, 收藏, 编辑
std::function是可调用对象的包装器,它最重要的功能是实现延时调用:

#include "stdafx.h"
#include<iostream>// std::cout
#include<functional>// std::function
void func(void)
{
std::cout << __FUNCTION__ << std::endl;
}
class Foo
{
public:
static int foo_func(int a)
{
std::cout << __FUNCTION__ << "(" << a << ") ->: ";
return a;
}
};
class Bar
{
public:
int operator() (int a)
{
std::cout << __FUNCTION__ << "(" << a << ") ->: ";
return a;
}
};
int main()
{
// 绑定普通函数
std::function<void(void)> fr1 = func;
fr1();
// 绑定类的静态成员函数
std::function<int(int)> fr2 = Foo::foo_func;
std::cout << fr2(100) << std::endl;
// 绑定仿函数
Bar bar;
fr2 = bar;
std::cout << fr2(200) << std::endl;
return 0;
}

由上边代码定义std::function<int(int)> fr2,那么fr2就可以代表返回值和参数表相同的一类函数。可以看出fr2保存了指代的函数,可以在之后的程序过程中调用。这种用法在实际编程中是很常见的。
std::bind用来将可调用对象与其参数一起进行绑定。绑定后可以使用std::function进行保存,并延迟到我们需要的时候调用:
(1) 将可调用对象与其参数绑定成一个仿函数;
(2) 可绑定部分参数。
在绑定部分参数的时候,通过使用std::placeholders来决定空位参数将会是调用发生时的第几个参数。

#include "stdafx.h"
#include<iostream>// std::cout
#include<functional>// std::function
class A
{
public:
int i_ = 0; // C++11允许非静态(non-static)数据成员在其声明处(在其所属类内部)进行初始化
void output(int x, int y)
{
std::cout << x << "" << y << std::endl;
}
};
int main()
{
A a;
// 绑定成员函数,保存为仿函数
std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2);
// 调用成员函数
fr(1, 2);
// 绑定成员变量
std::function<int&(void)> fr2 = std::bind(&A::i_, &a);
fr2() = 100;// 对成员变量进行赋值
std::cout << a.i_ << std::endl;
return 0;
}
boost::condition_variable的使用
2015年03月12日 14:33:59 oiooooio 阅读数:4572 标签: c++ boost 多线程 condition_variable 更多
这篇文章介绍boost::condition_variable的使用。
主要是在多线程的情况下,一般来说boost::condition_variable是用来进行多线程同步的,下面的代码主要测试了notify_one和notify_all的使用。
调用notify_one的时候,启用一个线程。
调用notify_all的时候,激活所有的线程。
当频繁调用notify_one的时候,并不会一直调用唯一的一个线程(在多个线程的情况下),他会激活其他线程,所以优先使用notify_one。
当数据量小于线程数量的时候,如果调用notify_all则会造成其他线程的空运行。
在结尾,附上测试结果。
-
// ThreadTest.cpp : 定义控制台应用程序的入口点。 -
// -
#include "stdafx.h" -
#include "boost/thread.hpp" -
#include "boost/thread/condition_variable.hpp" -
#include "boost/thread/mutex.hpp" -
#include <thread> -
#include <iostream> -
#include <windows.h> -
boost::condition_variable _g_cv; -
boost::mutex _g_cv_mutex; -
bool _g_is_quit = false; -
void ThreadFunc_1() -
{ -
boost::mutex::scoped_lock lock(_g_cv_mutex); -
while (!_g_is_quit) -
{ -
_g_cv.wait(lock); -
printf("ThreadFunc_1.\n"); -
// Sleep(10); -
} -
} -
void ThreadFunc_2() -
{ -
boost::mutex::scoped_lock lock(_g_cv_mutex); -
while (!_g_is_quit) -
{ -
_g_cv.wait(lock); -
printf("ThreadFunc_2.\n"); -
// Sleep(10); -
} -
} -
void ThreadFunc_3() -
{ -
boost::mutex::scoped_lock lock(_g_cv_mutex); -
while (!_g_is_quit) -
{ -
_g_cv.wait(lock); -
printf("ThreadFunc_3.\n"); -
// Sleep(10); -
} -
} -
void ThreadFunc_4() -
{ -
boost::mutex::scoped_lock lock(_g_cv_mutex); -
while (!_g_is_quit) -
{ -
_g_cv.wait(lock); -
printf("ThreadFunc_4.\n"); -
// Sleep(10); -
} -
} -
void ThreadControl() -
{ -
#define THREADCOUNT 4 -
typedef void(*FUNC)(); -
FUNC threadArray[4] = { ThreadFunc_1, ThreadFunc_2, ThreadFunc_3, ThreadFunc_4 }; -
for (size_t i = 0; i < THREADCOUNT; i++) -
{ -
boost::thread th(threadArray[i]); -
} -
} -
int _tmain(int argc, _TCHAR* argv[]) -
{ -
ThreadControl(); -
using namespace std; -
int value = 0; -
while (true) -
{ -
printf("enter 1 to notify one thread.\n"); -
printf("enter 2 to notify thread one by one.\n"); -
printf("enter 3 to notify all threads.\n"); -
printf("enter <-1> to quit.\n"); -
cin >> value; -
switch (value) -
{ -
case -1: -
_g_is_quit = true; -
break; -
case 1: -
_g_cv.notify_one(); -
break; -
case 2: -
for (size_t i = 0; i < THREADCOUNT; i++) -
{ -
_g_cv.notify_one(); -
} -
break; -
case 3: -
_g_cv.notify_all(); -
break; -
default: -
printf("enter again.\n"); -
break; -
} -
if (_g_is_quit){ -
break; -
} -
} -
system("pause"); -
return 0; -
}
测试结果:
--------------------------------
enter 1 to notify one thread.
enter 2 to notify thread one by one.
enter 3 to notify all threads.
enter <-1> to quit.
1
ThreadFunc_3.
2
ThreadFunc_2.
ThreadFunc_1.
ThreadFunc_4.
ThreadFunc_3.
4
enter again.
3
ThreadFunc_2.
ThreadFunc_1.
ThreadFunc_3.
ThreadFunc_4.
本文涵盖C++面试关键知识点,包括虚函数实现、智能指针使用、STL容器原理、TCP/IP协议比较、数据结构算法解析及多线程同步技巧。深入探讨C++语言特性,详解智能指针shared_ptr与unique_ptr的运用场景与注意事项,同时提供条件变量boost::condition_variable的实用案例。
4万+

被折叠的 条评论
为什么被折叠?



