1. multiset操作
这儿是对STL各个容器的相关介绍. http://www.cplusplus.com/reference/stl/
multiset比较重要的操作就是: insert, erase, count和find
2. memcpy实现
void *Memcpy(void *dst, const void *src, size_t size) { char *psrc; char *pdst; if(NULL == dst || NULL == src) { return NULL; } if((src < dst) && (char *)src + size > (char *)dst) // 自后向前拷贝 { psrc = (char *)src + size - 1; pdst = (char *)dst + size - 1; while(size--) { *pdst-- = *psrc--; } } else { psrc = (char *)src; pdst = (char *)dst; while(size--) { *pdst++ = *psrc++; } } return dst; }
3. vector,list,map循环删除
循环删除vector和map中的元素
删除所有偶数项,并打印出删除的项
(1). vector/queue
正确方法1:
void erase(vector<int> &v) { for(vector<int>::iterator vi=v.begin();vi!=v.end();) { if(*vi % 2 == 0) { cout << "Erasing " << *vi << endl; vi = v.erase(vi); } else ++vi; } }
正确方法2:
void erase2(vector<int> &v) { for(vector<int>::reverse_iterator ri=v.rbegin();ri!=v.rend();) { if(*ri % 2 == 0) { cout << "Erasing " << *ri << endl; v.erase((++ri).base()); //erase()函数期待的是正向iterator,故而这里要调 //用base()函数将逆向iterator转换为正向的 } else ++ri; } }
(2).map/list
正确方法
void erase(map<int,int> &m) { for(map<int,int>::iterator mi=m.begin();mi!=m.end();) { if(mi->second % 2 == 0) { cout << "Erasing " << mi->second << endl; m.erase(mi++); } else ++mi; } }
4. mysql操作
5. 静态变量与类大小关系.
首先,类的大小是什么?确切的说,类只是一个类型定义,它是没有大小可言的。
用sizeof运算符对一个类型名操作,得到的是具有该类型实体的大小。
如果
Class A;
A obj;
那么sizeof(A)==sizeof(obj)
那么sizeof(A)的大小和成员的大小总和是什么关系呢,很简单,一个对象的大小大于等于所有非静态成员大小的总和。
为什么是大于等于而不是正好相等呢?超出的部分主要有以下两方面:
1) C++对象模型本身
对于具有虚函数的类型来说,需要有一个方法为它的实体提供类型信息(RTTI)和虚函数入口,常见的方法是建立一个虚函数入口表,这个表可为相同类型的对象共享,因此对象中需要有一个指向虚函数表的指针,此外,为了支持RTTI,许多编译器都把该类型信息放在虚函数表中。但是,是否必须采用这种实现方法,C++标准没有规定,但是这几乎是主流编译器均采用的一种方案。
2) 编译器优化
因为对于大多数CPU来说,CPU字长的整数倍操作起来更快,因此对于这些成员加起来如果不够这个整数倍,有可能编译器会插入多余的内容凑足这个整数倍,此外,有时候相邻的成员之间也有可能因为这个目的被插入空白,这个叫做“补齐”(padding)。所以,C++标准紧紧规定成员的排列按照类定义的顺序,但是不要求在存储器中是紧密排列的。
基于上述两点,可以说用sizeof对类名操作,得到的结果是该类的对象在存储器中所占据的字节大小,由于静态成员变量不在对象中存储,因此这个结果等于各非静态数据成员(不包括成员函数)的总和加上编译器额外增加的字节。后者依赖于不同的编译器实现,C++标准对此不做任何保证。
C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1。
如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable,在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。而虚函数本身和其他成员函数一样,是不占用对象的空间的。
5. 构造函数中使用memset(this,0,sizeof(*this))初始化类.
memset在c中是用的非常频繁的初始化函数了,当然也被带到了C++当中,因为当有如下类涉及到非常多的成员变量,很多coder经常偷懒改用memset在构造函数当中初始化
struct Test { int _1; int _2; long _3; ... Test(){memset(this,0,sizeof(Test));} };以上如果所有成员变量是简单的内置类型是没有问题,但是可能某次需求迫使你需要往Test中增加一个数组,如下
struct Test { int _1; int _2; long _3; ... std::vector<int> _n; Test(){memset(this,0,sizeof(Test));} };结果在不同的编译器是不同的,最好的情况当然是启动时程序就挂掉了,至于为什么会挂掉,明白vector中实现了什么就知道了,当然不仅仅是vector,其他stl或者自己定制的容器可能都存在这个问题。
上面是一个陷阱,再一个陷阱就是在派生类的构造函数当中使用memset的问题
class Base { int _a; }; class Derive : public Base { public: Derive(){memset(this,0,sizeof(Derive));} };以上代码看出问题了没?
如果在改成下面的呢?
class Base { int _a; public: virtual ~Base(){} };memset做了一件本来你是做不到的事情,那就是把Derive的虚表指针也之位0了,结果当然就是内容泄露了
总结:
memset在内存操作方面太灵活了,但是我们也得注意在c++使用的时候是存在很多陷阱的,稍有不慎可能会造成很大的隐患,以上的问题并不是马上就一定会暴露出来的,不同的编译器现象是不一样的
5. 使用C++写成尽可能多的死循环方式.
C/C++中三种死循环的写法
一般来说,我们都会避免在程序中出现死循环。但在某些情况下我们又需要死循环,因此特总结三种常用的死循环的写法如下:
(1)、
while (1)
{
........
}
(2)、for(;;)
{
....
}
(3)、Loop:
....
Goto Loop;
6. C++类默认生成的函数.
ISO/IEC 14882(C++的国际标准文件)中说明:
一个空类必须默认生成四个成员函数:
构造函数,析构函数,拷贝构造函数,赋值函数
class Empty {
public:
Empty(); // 缺省构造函数
Empty(const Empty& rhs); // 拷贝构造函数
~Empty(); // 析构函数
Empty& operator=(const Empty& rhs); // 赋值运算符
};
有一点争议的是:
在《effective c++》中,大师说到一个类中应该包含六个默认成员函数,另外两个是
取址运算符和常取址运算符
Empty* operator&(); // address-of operators
const Empty* operator&() const;
7. 编译过程包括什么,哪些可以本地完成,哪些可以分发出去完成.
编译过程分为四步:
一、预处理工作:处理宏定义,不管是由-D参数指定,还是在源码内部通过#define,或者使用了标准库,扩展库中的宏,都会替换为定义的值。
命令:cpp a.c>a.i 输出的a.i就是预处理过的文件,包含了完整的内容。
由此可以得出,宏不是运行时的定义内容,也不是编译时的内容,而是预处理阶段就完成的。
二、编译阶段:将源代码(也就是预处理过的代码)编译为特定机器的汇编语言。
命令:gcc -S -Wall a.i 输出为特定的汇编内容。
三、汇编阶段:将汇编源码汇编为机器码内容。
命令:as a.s -o a.o 此处如果有对外部的函数使用,则会预留未定义的地址,以供最后一步链接来使用。
四、链接阶段:将链接对象文件(上编译汇编后的文件)链接为可执行文件,此过程比较复杂,需要链接很多外部的库文件,包括静态的,动态的库。
命令:ld -dynamic-linker .. .. ..
可以使用gcc a.o 来简化上一过程。
最后得出的是可执行文件。
8. TCP/IP过程
表1-2 TCP/IP模型各个层次的功能和协议
层次名称
功 能
协 议
网络接口
(Host-to-Net Layer)
负责实际数据的传输,对应OSI参考模型的下两层
HDLC(高级链路控制协议)
PPP(点对点协议)
SLIP(串行线路接口协议)
网际层
(Inter-network Layer)
负责网络间的寻址
数据传输,对应OSI参考模型的第三层
IP(网际协议)
ICMP(网际控制消息协议)
ARP(地址解析协议)
RARP(反向地址解析协议)
传输层
(Transport Layer)
负责提供可靠的传输服务,对应OSI参考模型的第四层
TCP(控制传输协议)
UDP(用户数据报协议)
应用层
(Application Layer)
负责实现一切与应用程序相关的功能,对应OSI参考模型的上三层
FTP(文件传输协议)
HTTP(超文本传输协议)
DNS(域名服务器协议)
SMTP(简单邮件传输协议)
NFS(网络文件系统协议)
说明 TCP/IP与OSI最大的不同在于OSI是一个理论上的网络通信模型,而TCP/IP则是实际运行的网络协议。
为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的 ACK报文和FIN报文多数情况下都是分开发送的。