c++

1.单例模式如何写

   模板方法模式(NVI手法,非虚函数接口)其实通过非虚函数内调用虚函数。非函数作为虚函数的外覆器。
模板方法模式,某件事次序相同,比如点单,吃东西,买单,其中吃东西有不同,点单,买单相同,因此通过在父类定义一个non-virtual 函数,基本步骤包括,点单,吃东西,买单,不过吃东西为虚函数,子类重写,以代表自己的吃东西类。
    strategy模式:主要即是函数指针成员变量,该函数指针成员变量可以指向外部函数,也可以指向宁一个继承体的对象,
    一些c++问题一些c++问题

   工厂模式:

2.类const成员函数含义(与this指针联系)
3.编译与运行的区别
4.四种强制类型转换
  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
5.智能指针如何写
主要针对类的成员变量中含有指针,有三种实现方法:
  • 常规指针行为,即值copy,无需特殊处理
  • 只能指针,所指对象是共享的,可以防止悬垂指针
  • 类采取值型行为,即每个对象是唯一的
6.RTTI(运行类似识别)
  • dynamic_cast
  • typeid
7.内存
1.allocator
2.opearor new,operator delete 
3 implacement new expressio (定位 new 表达式)

8.指向类成员的指针以及指向类成员函数的指针
type class::*p
type (class::*p)(type, type)

9.union(联合),enum(枚举),镶嵌类,
10.多重继承,虚拟继承
11.lambda表达式
【capture】(param){}
返回值由{}内解析,如果出现return,则有返回值,没有相当于void function();
capture 捕获外部变量,常用【a】【&b】【=】【&】【this】【】,最后一个代表不捕获外部变量。

12.c++
c++其实由四个次语言组成,次语言,对象驱动c++,模板泛型编程,stl,涉及效率的时候针对每个次语言,都有他们各自的特点,比如对于c,内置类型以值传递快与引用,但是对于c++,传递对象时,传递引用效率高,但是对于stl,函数对象以及迭代器都是以指针实现,因此又是以值传递更快。针对不同语言,规范不同。

13.const  于 #define 的区别。
关键字:预处理器,编译器,常量,宏

14.inline
优点,缺点。
注意inline只是对编译器的申请,并不是强制命令,可以隐喻申请(类定义体内定义函数),可以显示申请(inline),一般inline函数一定定义在头文件内,因为替换函数本体需要知道函数长什么样子(模板同样),太复杂的函数(循环,递归,虚函数)一般会拒绝inline,inline函数同样不能调试。
函数是一种对象,但是inline只是简单的代码替换。

15.public继承与复合以及private继承的意义区别
public继承意味着is-a关系,表示对于基类能做的任何一件事,派生类同样能做
复合意义有两个:在应用域表示has-a关系,在实现欲表示的是根据某物实现。
private继承实现意义根据某物实现。只有实现部分被继承,接口是private

16.区分接口继承和实现继承(public继承下)
public继承下,派生类总是继承基类的接口,三种函数的继承意义:
pure virtual 函数,只具体指定继承接口
impure virtual 函数,指定继承接口以及缺省实现
non-virtual 函数指定继承接口以及强制性实现。

17.c++默认写了哪些函数

18.保证变量在使用前初始化,
1.手工初始化内置对象
2.区分构造函数的初始化(列表)与赋值的区别。特别是const成员以及引用成员,必须用初始化列表。
3.不同编译单元内定义的non-local static对象的初始化次序是不确定了,因此,为了在使用一个已经初始化的static对象前,保证该static对象已经初始化了,常用实现singlton模式,即将non-local static 对象移置到loal-static对象,local-static对象会在该函数被调用期间,“首次遇上该对象的定义式时被初始化,如果你从未调用该函数,就绝不会产生构造和析构成本”

19.static函数生命期,从构造出来到程序终止。

20.const
1.const变量
2.const指针
3.const迭代器
4.const成员函数(可重载)
意味着什么?
1.biwise constness
2.logical constness
由此引出一个关键字,mutable ,mutable释放掉non-static成员变量的bitwise-constness约束。
const和non-const成员函数中避免重复的关键技术。
一些c++问题

21.对象管理资源。常用类:auto_ptr,tr1::shared_ptr,为合理管理资源,利用RAII技术(资源取得时机即是初始化时机),auto_ptrptr(new class),auto_ptr与shared_ptr,weak_ptr的区别。

22.c/c++程序生成过程,预处理-》编译-》汇编-》链接-》生成可执行文件,
讲讲预处理与编译区别。

23.external 关键字的含义,以及external “c”的用法

24.如果不想编译器合成函数的话,那么就将copy构造函数,copy 操作符声明为private,并且不给予实现。或者提供一个uncopyable基类。

25.operator = 避免自我复制,
有些很明显 x = x ,有些很隐晦,px【i】 = px【j】;
确保当对象自我赋值时operator=有良好行为,技术有来源对象与目标对象地址比较,良好的语句顺序,以及copy and swap 技术

26.深入理解c++构造函数语义学

27.避免遮掩继承而来的名称,可以看作为基类成员,派生类成员作用域的知识点,当发生遮掩的时候,唯一的就是名字(成员名字,函数名字)与函数类型以及是否为虚无关。为了使基类名字可见,用using Base::func

28.绝不从定义从基类继承而来的virtual 函数的默认参数,为什么?首先分析动态绑定,静态绑定,静态绑定指对象声明时的类型,而动态类型指对象真正所指的对象,然而virtual 函数是动态绑定的,但是默认参数却是静态绑定的,因此会出现,执行派生类的virtual函数,但是默认参数却是基类的参数值

29,切记成员变量声明为private

30.宁可拿non-member non-friend函数替代成员函数,这样可以增加封装性,以及弹性。
成员函数的对立面是non-member函数,不是friend 函数

31.标准库的swap函数,以及stl每个容器的swap成员函数,以及相应的swap特化。

32.static_cast<>(type).func(),做转型时,其实是在type的副本上做改变,type本身没有变化。

33.句柄类(通过指针成员指向宁一个类的具体实现),接口类。


34.用对象管理资源,以及copy-swap技术的使用。

35.模板与class的区别:
一些c++问题

36.typename 与 class的区别,以及typename用来标识嵌套从属类型名称的作用。

37.类的隐士转换(如果构造函数有一个形参),以及explicit作用。

38.什么样的类为函数对象。标准函数库对象有哪些,怎么使用,以及函数对象的两个函数适配器?(绑定器,求反器)(bind1st,bind2nd,not1,not2)如何实现?

39.模板的实例化概念,模板实参推测(严格要求一致性),
模板的显示实参。何时实例化类以及实例化类成员函数。
模板特化,何时用模板特化,(函数模板特化,以及类特化)

40.下标操作符重载,主要知识点返回引用

41.重载成员访问操作符(*,->)特别是->

42.声明与定义区别:
注意分析为什么头文件放类的声明,而在一个源文件放类的定义。避免多个文件包含多个定义
类型声明,非初始化的external 声明式子都是定义(定义式声明) 
函数声明,没有{}函数体则为声明
类声明 class CA;类定义class CA {};
注意类内的static 变量是声明;

43.注意STL的工作方式是copy,即保存的是对象的副本,从容器取出也是副本,删除,插入,移动都是副本。

44.异常
主要四点:
throw(表达式)表达式类型决定异常类型
try{     捕获
catch(异常说明符){} 处理
catch(异常说明符){}
}
标准异常类其实是一个继承体系。每个异常对象有一个what成员用于详细说明异常的文字描述。
下面详细介绍上述四个关键地方:
深入理解异常,需要理解下面三个地方:
1.抛出异常时发生什么?

首先抛出异常对象会激活具体的catch处理代码,该catch的选择是沿着函数的调用链来选择的,因此可能会将程序的控制权从一个函数传到宁一个地方,因此表示两点:1.沿着函数链的函数提早退出。2.会释放局部存储。因此深入理解异常对象,异常对象是个特殊对象,通过throw表达式初始化了一个特殊的异常对象,并且驻留在可能被激活的任意catch可以访问的空间,然后将该异常对象传递给catch,该对象由throw创建,并初始化为抛出的表达式的 副本 因此异常对象是必须能复制的类型。
异常对象与函数参数类似,因此注意下面几点:
1.抛出的对象类型的静态编译类型决定,因此可能会导致派生类到基类的分割。
2.栈展开指沿着函数调用链找catch自居,在栈展开期间,提早退出包含throw的函数以及调用链的其他函数,因此会有局部对象的销毁以及析构函数的发生,如果在析构函数的时候又有异常发生则会terminal 函数,结束程序。因此析构函数不应该抛出异常。构造函数发生异常,可能初始化对象不完整,应该通过RAII技术消除该问题,  资源分配即初始化。

2.捕获异常时发生什么?
catch匹配,匹配的是第一个可以匹配的catch块,即应该很好的设计catch快的顺序。
catch说明符可以是引用以及非引用,非引用表示复制原始的异常对象。
总之除了需要完全匹配外,其他如函数参数很类似。
重新抛出  throw ,catch 可以通过重新抛出throw将异常传递调用函数链的更上层函数。
捕获所有异常 catch(...)
3.用来传递错误的对象的含义?

45.右值引用于move语义
1.首先识别何为左值,何为右值:左值可以理解为一个标识一块内存的位置,并且可以通过地址符取该内存的地址
2.move语义:
考虑函数 X foo()  x = foo(), 发生三步第一步析构x原始成员,然后operator=,然后析构foo()产生的临时对象,
move语义希望可以直接转换x与临时对象,即swap(x,foo())。
3. 右值引用,比左值引用多了一个&,即 int  && 。
注意下面一点。int & x = foo()  错误,
  但是 const int & = foo()是可以的。
即const 左引用可以绑定到右值上。
但是对于函数fun(const int & x) 不能区分传入的是左值还是右值, 因此也就不能够用move 语义了。
4.强制move 语义
可以通过std::move(type a) 将 无论是左值还是右值都变为右值。
5.右值引用是不是右值?
当右值引用有名字时,则为左值,否则为右值
foo(int && x)  x 为左值
int && foo()  foo()为右值
因此对于基类base有右值拷贝构造函数,现在devried继承base,如果想实现devied的右值拷贝构造函数
deviride(devierd && de):Base(de)则有问题,因为此时de为一个左值,不会调用基类的右值拷贝构造函数,应该这样写
:base(move(de))

46.标准io库
注意几点:
1.iostream的继承体系,ostream-ofstream,ostringstream,iostream,istream-ifstream,istringstream,iostream。 iostream-stringstream,fstream。
2.不可复制,原因?
3.流状态,文件结束状态 eof(),系统级错误状态不可恢复bad(),以及可以恢复错误状态fail()。
常用检查流状态 if(cin) while(cin>>word)
4。每个流管理一个缓冲区,缓冲区何时刷新(程序正常结束,缓冲区满了,在写入下一个内容之前刷新,操作符flush,endl,ends显示刷新,unitbuf在每次输出操作完成刷新)
5.文件流与文件绑定,两种方式将文件与文件流绑定,1,直接构造函数ifstream if(filename),if.open(filename),检测文件打开成功与检测输入输出流状态一样if(ifstream),文件流重新捆绑宁一个文件,应先close现在的文件,if。close(),然后再调用if.open之前先调用,if.clear(),清空文件的状态标识,因为if.close()只是关闭之前的文件,并不会改变文件流的状态标识,最后在文件流与文件绑定的时候有文件模式,文件模式一般用默认值。
6.字符串流,stringstream将存储在程序内存中的string对象捆绑起来,几个主要函数,stringstream(strnig s),stringstream.str(),stringstream.str(s).
总结其实就是iostream与输入输出设备捆绑,fsteam与文件捆绑,stringstream与内存中的string捆绑。
7一个函数getine(cin, string)将cin输入的内容存到string中。
用法 string line word
while(getline(cin,line))
istreamstream strem(line)
while(line>>word)。。。

47.关键字mutable。 volatile
只能用在类的non-static ,non-const 成员身上,这样的成员在const成员函数内可以改变
mutable int a
void fun() const { a++;} // ok;

48.指针与引用区别。
主要体现在以下几点:
1.首先指针可以为null,但是引用不可以,引用不能为null ,必须引用到某个对象身上。因此导致指针可以不设置初值,但是引用不行。
2. 引用绑定到某个对象身上后, 即不会改变,一直绑定在该对象身上。指针可以指向不同对象。
3.对于一个特殊的操作符重载operator[]因该返回引用

49.不要以多态处理数组?
当数组下标计算时,是按照,sizeof(Base )*i,但是如果以多态处理数组时,就会出现下标计算混乱的错误。

50.默认构造函数
对于一个类如果什么构造函数都没有的时候,编译器不会任何情况下都会产生一个默认构造函数,或者说产生的默认构造函数是没有用的,编译器仅仅在有必要的时候合成有用的默认构造函数。下面四种情况会合成:
1.带有默认构造的类成员对象。
2.带有默认构造的基类
3.带有虚函数
4.带有虚基类
如果提供多个构造,但是都没有默认构造,编译器会根据不同情况扩张已存在的构造函数。

默认复制构造函数:同上,只有当类不展现出bitwise copy semantics 时编译器会合成一个,四种情况不展现bitwise copy semantics ,同上。关于复制构造函数设计一个VRV(返回值优化),特别是以值的方式传回一个对象。那么提供一个explicit inline 可以提供NRV优化。
关于成员的初始化式子:注意几种情况必须提供成员的初始化:初始化引用成员,常量成员,调用成员类或者基类的构造,且有一组参数。注意构造函数内部成员初始化与初始化式子的区别。
  word():str(0){} == word(){str(0}
word(){str = 0} == word(){string str; string temp(0) ; str = temp;}
当用初始化式子时,编译器实质上是做了将初始化式子在函数体内填充,不过位置在自己写的式子前面。

51.非必要不提供默认构造,
不提供默认构造,三种情况不适用
1.产生一个对象数组,
2.不适用与基于模板容器的类
3.virtual base class,如果没提供默认构造,因此需要每一层派生类都得知道base class 的结构。
52.隐式类型转换
两种情况出现隐式类型转换:
1.单自变量的构造函数,
2.operator type()
隐式类型转换有危害,应该消除,对于隐式类型转换符,可以用其他函数代替,例如string::c_str()函数,对于单自变量的构造函数,可以通过声明该构造函数为explicit消除隐式类型转换的危害。
53.前置后置操作符重载编写区别,及其原因。
54.逗号表达式,逗号左侧先评估,右侧后评估,然后结果以逗号右边的值为代表。
55.缓式评估(拖延战术)的几个用途:
1.引用计数
2.区分读写
3.缓式取出:不用一次性读取所有资源,可在资源必须使用时才读取
4.表达式缓评估
与之对应的是宁一种技术,超前评估,利用空间加快速度,比如说求平均值,一直维护平均值,或者vector的预留内存技术。

下面几种实用编程技术:
1.construct 和 non-member functions 虚化;
有一种虚函数对于返回值没有要求。返回值可以返回各自派生类或者基类的指针。
对于non-member function虚化,其实就是non-member function内部通过对象重新调用虚函数。
2.允许类产生对象数量:
首先单例模式,然后改进可以产生任意数量个类对象,但是对于派生类,或者类对象作为宁一种对象的组成成分时有问题,因此利用伪构造方法。将原始构造private,即不可以做基类。
一种情况:
ca*p = getca;
delete p;
ca *p = getca; //error;
解决办法将计数与伪构造结合。即产生对象必须通过伪构造。
主要技术其实就是,既然不想通过construct构造函数,即将这些函数声明为private,但是生成对象的时候通过伪构造生成对象。

3.要求或者禁止对象产生于heap中。
首先要求对象产生于堆中,即让隐士调用的构造和析构private,最好是只令析构private,取代用伪析构函数destroy()函数代替,然后如果是作为基类,即将析构从private改为protected,然后如果作为某类对象的情况,则改用指针。
禁止对象是否为于heap一般很难做到,但是可以设计类的指针删除动作是否安全比指针是否指向heap简单,通常指针删除动作合理与否与该指针是否是由new返回的相关,因此实现一个混合抽象基类,其中operator new 操作符,调用全局operator new, 但是将产生的指针放到vector 或者list内,然后operator delete操作符重载函数在调用operator delete之前先在list内看看是否能先找出该指针
禁止对象产生于heap中,主要将operator new, delete private。

3.智能指针(smart point)
主要三个地方,构造析构,复制,赋值,解引,对于判断智能指针是否为NULL,一般通过operator !操作符重载。对于继承体系的智能指针,通常用类内函数模板的一个技术,来转型。

4.引用计数
其实类内含一个其他的结构体,或者类,该内含类对象包括正真的数据,并且拥有引用次数,然后真正的类操作引用次数。其实还有其他操作手法,即智能指针和能产生任意数量的对象的技术混合。

5.代理类。
几个作用:
1. 实现多维数组。
2. 压制隐式转换,即外层类,内层类,单参构造参数为内类,内类用普通的单参。
3.区分左值引用和右值引用,常用在operator[]操作符重载上。令operator[]返回代理类对象,而不返回字符本身,然后等待,看这个代理类如何使用,如果被读,则可以将operator[]看为一个读动作,如果写,则将operator[]看做一个写操作。

6.让函数根据一个以上的对象类型决定如何虚化。

深入探讨c++对象模型:
1.关于对象:
相对于c,c++对象的成本,首先明确c++对象的内存组成,分为三部分,non-static 成员,内存对齐,以及支持virtual 而产生的负担,因此只有在设计virtual function,以及virtual base class 时才会设计额外负担。
然后了解c++对模型,由数据成员,加上了vptr构成,其中vptr指向虚表,虚表的第一个表格一般指向关于RTTI相关内容的指针,其他则是正真要执行的虚函数指针,其中vptr的设定一般由类的构造,析构,copy assignment operator 自动完成。
为何只有指针和引用才支持多态,因为他们不会引发内存中任何与内存的委托操作(比如说基类= 派生类会发生切割),不同的指针类型,所占内存,存储内容都没有差别,真正差别的是不同指针类型对于内存的不同解释。

2.Data
空类的大小为1:这样的话两个不同的类对象有不同的 地址。
类大小分析及布局分析:non-static 成员,内存对齐,以及基类完整性带来的额外大小。当引入virtual 函数,或者virtual base class带来的额外大小。A.a 和p->a的成本分析,这两种情况取a只有在virtual base class,并且a为virtual base class的情况下才有成本差别,因为这时候p-》a,p的指向的类不能确定,需要通过中间层得到。
当一个类引入virtual function时带来的代价。虚表,虚指针,构造和析构对于vptr 编译器需要特别处理。
虚拟继承的实现,其实通过vptr指向virtual table,通过索引负值得到虚基类成员,索引正值得到虚函数。

对于类,三种函数,non-static function, static function, virtual member function。对于非静态成员函数,效率其实与non-member函数一样,通过编译器转化了,引入了一个参数 this,然后函数体内对成员的引用都是通过this->调用的。并且还会有一个函数命名过程,其实就是将函数名根据函数名和参数转化一下。对于虚成员函数调用,其实也是通过了一个转化,ptr->a()  == (*ptr->vptr[1])(ptr)。当然如果类直接引用虚拟成员的话,就不需要通过这一步转化。静态成员其实与non-member函数类似,不过限定域。
然后主要谈下virtual 函数的实现原理,其实主要就是通过指针或者引用调用virtual 函数的时候,发生 ptr->a()  == (*ptr->vptr[1])(ptr)这样类似的转化,ptr实际指向什么类,因此执行什么类型的a函数只有在运行期才能知道。对于多重继承下的virtual 函数,就比单继承复杂些,这时候需要发生指针的offset ,指向具体base 类的入口。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值