今天去了一家上市的公司面试C++岗位。笔试题感觉还好,初试的时候问了一下概念的东西,就被难住了,有的的确是没有用到过,有的是知道怎么用但是不知道怎么组织语言。虽然干了好多年了,但是由于半路出家,对于一些基础知识没有太注重,基本上是遇到那一块才会去了解这些。以后要系统的学一遍基础知识,常用的设计模式,网络编程也要注视起来。
总结一下这次遇到的几个问题。
- 内存的分配情况
- 指针和引用的区别
- 浅拷贝和深拷贝
- 开多少线程最优
- 观察者模式
一、内存的分配情况:
C、C++内存分配方式有三种:
(1)从静态存储区域分配:
内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static变量等。
(2)在栈上分配:在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。(3)从堆上分配:即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
一个C、C++程序编译时内存分为 5大存储区:堆区、栈区、静态存储区(全局区)、文字常量区、程序代码区。
(1) 程序代码区这个很简单,代码要执行,肯定要加载进内存, 我们不必关心。(2) 文字常量区一般我们这样定义一个字符串时,其是在文字常量区的:char* s1 = "hello, world";char* s2 = "hello, world";if(s1 == s2)printf("s1和s2指向同一个在文字常量区的字符串");这里, s1和s2指向的是同一个字符串3. 静态存储区全局变量,静态变量会放在这个区域,事实上,全局变量也是静态的。以上1,2,3三个区域的内存在程序起来的时候就开辟好了的。4. 栈局部变量就是在栈里的。另外,函数调用时的参数也是在栈里的,这个现在不必关心5. 堆malloc或new出来的内存就是在堆里的,需要程序员自己管理清除。
二、指针和引用的区别
相同点:
都是地址的概念;
指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。
区别:
1. 指针是一个实体,而引用仅是个别名;
2. 引用使用时无需解引用(*),指针需要解引用;
3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
4. 引用没有 const,指针有 const,const 的指针不可变; 定义引用的时候可以const啊
5. 引用不能为空,指针可以为空;
6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
typeid(T) == typeid(T&) 恒为真,sizeof(T) == sizeof(T&) 恒为真,但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定)。
7. 指针和引用的自增(++)运算意义不一样;
引用的一些规则如下:(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
三、浅拷贝和深拷贝
如果类没有自定义拷贝构造函数,C++编译器自动会产生一个默认的拷贝构造函数。这个默认的拷贝构造函数采用的是“位拷贝”(浅拷贝),而非“值拷贝”(深拷贝)的方式,如果类中含有指针变量,默认的拷贝构造函数必定出错。
四、开多少线程最优
这个有共识的和CPU核数有关。一般控制到CPU核数的1-2倍。
五、观察者模式
Observer 模式应该可以说是应用最多、影响最广的模式之一。Observer 模式要解决的问题为: 建立一个一( Subject)对多( Observer) 的依赖关系, 并且做到当“一” 变化的时候, 依赖这个“一”的多也能够同步改变。MVC就是一个Observer的实例。
应用场景: 对同一组数据进行统计分析时候, 我们希望能够提供多种形式的表示 (例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。这些表示都依赖于同一组数据, 我们当然需要当数据改变的时候, 所有的统计的显示都能够同时改变。 Observer 模式就是解决了这一个问题。
UML类图:
Subject(目标,这个只有一个)
——目标知道它的观察者。可以有任意多个观察者观察同一个目标;
——提供注册和删除观察者对象的接口。Observer(观察者,可以有很多个)
——为那些在目标发生改变时需获得通知的对象定义一个更新接口。ConcreteSubject(具体目标)
——将有关状态存入各ConcreteObserver对象;
——当它的状态发生改变时,向它的各个观察者发出通知。ConcreteObserver(具体观察者)
——维护一个指向ConcreteSubject对象的引用;
——存储有关状态,这些状态应与目标的状态保持一致;
——实现Observer的更新接口,先通过ConcreteSubject获取目标的状态,然后使自身状态与目标的状态保持一致。
代码实现:
#include <iostream> #include <list> using namespace std; class CSubject; //观察者的超类 class CObserver { public: virtual void setSubject(CSubject* psubject) = 0; virtual void Update(int status) = 0; }; //目标的超类 class CSubject { public: virtual void Attach(CObserver* pobserver) = 0; virtual void Detach(CObserver* pobserver) = 0; virtual void Notify() = 0; }; //具体的观察者 class CCocreateObserver :public CObserver { public: void setSubject(CSubject* psubject) { m_subject = psubject; }; void Update(int status){ cout<<"CCocreateObserver get the update, new status:"<<status<<endl; } private: CSubject* m_subject; }; class CCocreateObserver2 :public CObserver { public: void setSubject(CSubject* psubject) { m_subject = psubject; } void Update(int status) { cout<<"CCocreateObserver2 get the update, new status:"<<status<<endl; } private: CSubject* m_subject; }; //具体的目标 class CCocreateSubject:public CSubject { public: void Attach(CObserver* pobserver) { m_listObsrver.push_back(pobserver); pobserver->setSubject(this); } void Detach(CObserver* pobserver) { m_listObsrver.remove(pobserver); } void Notify() { std::list<CObserver*>::iterator it = m_listObsrver.begin(); while(it != m_listObsrver.end()) { (*it)->Update(m_iStatus); it++; } } void SetState(int state) { m_iStatus= state; } private: list<CObserver*> m_listObsrver; int m_iStatus; }; int _tmain(int argc, _TCHAR* argv[]) { //创建目标 CCocreateSubject *psubject = new CCocreateSubject(); //创建观察者 CCocreateObserver *pobserver = new CCocreateObserver(); CCocreateObserver2 *pobserver2 = new CCocreateObserver2(); // psubject->Attach(pobserver); psubject->Attach(pobserver2); //改变状态 psubject->SetState(5); psubject->Notify(); psubject->SetState(8); psubject->Notify(); psubject->Detach(pobserver); psubject->Detach(pobserver2); delete pobserver; delete pobserver2; delete psubject; getchar(); }
输出结果: