智能指针:多线程访问共享对象。
强弱智能指针。
智能指针使用在多线程环境中。
强烈推荐一本书《Linux多线程服务端编程module网络库》看第一章。
编写线程安全的类。
竞态条件。由于CPU调用顺序不同,结果不同。
内存:数据段、堆、栈。多线程共享的是数据的和堆。栈是每个线程所私有的。
线程的运行可以看做并发的运行。
运行结果不可预期。
多线程环境中,操作共享对象。在访问共享对象的时候,另外的线程可能正在析构或已经析构完成。
解决办法?
怎样在多线程环境下,安全的访问共享对象?
在创建的时候,使用强智能指针持有,其他线程使用弱智能指针持有。
到处使用强智能指针会发生交叉引用。
share_ptr<A> ptr1(new A());:(定义处)
week_ptr<A> pw=>obj;
share_ptr<A> sp=pw.lock();引用计数的操作时原子操作。(弱智能指针的提升)
智能指针出作用域前会被析构掉。
包含系统的头文件,相当于你在搞系统开发呢。
#include <windows.h>
包含了创建线程的方法。
不传参数就写NULL。
线程创建了对象。
sleep的单位是毫秒。
公司里面写东西必须先写文档,理清思路。
堆上的资源是共享的。
多线程访问共享对象。
class Test
{
public:
Test(){ cout << "Test()" << endl; }
~Test(){ cout << "~Test()" << endl; }
void test(){ cout << "call Test::test" << endl; }
};
//线程参数的结构体类型
struct ThreadData
{
ThreadData(shared_ptr<Test> sp) :wp(sp){}
weak_ptr<Test> wp;
};
//子线程
DWORD WINAPI threadProc(void *lparg)
{
::Sleep(2000);
ThreadData *p = (ThreadData*)lparg;
//p->test();
//通过weak智能指针的提升,来检测共享对象的存活状态
shared_ptr<Test> sp = p->wp.lock();
if (sp != NULL)
{
cout << "提升成功了!" << endl;
sp->test();
}
else
{
cout << "提升失败了" << endl;
}
delete p;
return 0;
}
void func()
{
//Test *p = new Test();
shared_ptr<Test> sp(new Test());
cout << sp.use_count() << endl;
ThreadData *data = new ThreadData(sp);
//创建一个线程 类似pthread_create
::CreateThread(NULL, 0, threadProc, data, 0, NULL);
//::Sleep(2000);
//delete p;
//::Sleep(5000);
}
int main()
{
func();
::Sleep(5000);
return 0;
}
内存池的开发在底层开发中,是很重要的一部分。
空间配置器,给了三篇博客,下去好好看。
小块内存频繁使用,用内存池进行处理。
大块内存知己用malloc和free。
SGI STL内存池。以8的倍数进行增长。
空间配置器:分配内存、释放内存、构造对象、析构对象。
栈上的对象不管是正常结束还是异常结束,出作用域一定会析构。
静态链表,每个节点的内存都是连续的。
每个节点(chunk)的前4个节点记录了下一个节点的内存起始地址。
多核,做到真真正正的并发。
用通俗的语言把复杂的问题解释清楚。
class默认是私有继承。struct默认是共有继承。
在c语言中,空struct是0,c++中是1.
面试不要跟考试一样。
在c++和C语言中,编译器生成符号的方式不同,所以必须在,调用时告诉他,以什么符号去找符号。
面试的时候要有自信,不要想那么多,要了要不要算了。
析构函数写成虚析构函数。虚函数就得去虚函数表中去找。
栈上的对象出作用域,自己进行析构。
基类指针指向堆上的派生类对象。就得写成虚函数。派生类析构自己的基类成员对象时会去调用基类的析构函数。这样就会导致派生类对象的析构函数调用不到,会导致资源泄漏。这种情况下就应该写成虚函数。
运行时的绑定是多态的基础。
哈希表的增删查效率非常高。
连续删除、插入迭代器会失效。如何防止,insert和esaear返回迭代器类型。
c++This指针是干什么用的:指向自身。方法就是靠this指针区分不同对象的方法。
基类对象的成员函数,全部写成纯虚函数。基类不想让它实例化。纯虚函数不能实例化。运行时纯虚函数表都保留着只读数据段中。
静态函数不能写成虚函数。
当内联(只是对编译器的建议)函数被修饰成虚函数,就不会内联。
函数的调用链。
抛出异常。
catch();用来接收、处理异常。
出了作用域,在哪里都找不到局部作用域中的变量。
智能指针可以帮你保证资源的释放,不会发生资源泄漏。
try{(可能发生异常的代码)
代码1:
代码2:
}
catch(string ***)
{
}
异常是可以抛的,一级一级的往上抛。
如何防止内存泄漏:智能指针。
dqueue,图画的非常好。deque源码完整分析(带图例)
重点在容器、智能指针那块。
new和malloc有什么区别?
运算符,因为它确实不是一个函数。它们的执行都会调用一个运算符的重载函数。
new的底层也是调用malloc。
void *:释放的内存和类型无关。
库都是动态链接的。如果你自己提供了new和delete的运算符的重载函数。它就会调用你自己的运算符重载函数,而不去链接库中的。
free不需要指定长度,长度在内存块中记录着。
4个字节记录对象的个数。
开辟单个对象不用记录对象的个数。
构造函数、析构函数的都有this指针,它要知道自己关联的是哪个对象。
都没有从正确的地方开始释放。
当自定义类,并提供了析构函数,则delete ptr;和delete []ptr;不能混用。
编译器的实现者。
int *p=new int;
用拿到的p并不是底层真真正正开辟的内存的起始地址,它返回的是,对象的起始地址。