C++基础知识点总结五
2017-10-14 21:59
267 查看
虚继承:虚函数继承和虚继承是两个不同的概念,
如图,D继承B和C,B和C同时继承A,那么图4中在类D中会出现两次A,为了节省空间,将B和C对A的继承定义为虚拟继承,A就成了虚拟基类
因此虚继承算sizeof(类)得到的结果和直接继承不一样,看清继承的时候是直接继承还是虚继承
例题一
如果是如上的虚继承,求sizeof(A),sizeof(B),sizeof(C)
答:
直接继承:public
A中char k[3]大小为3,做一次数据对齐后(编译器以4倍数为对齐单位)char k[3]所占大小为4,加上vptr指针,sizeof(A)为8
B中vptr为4字节,加上自己的char j[3]为4字节,加上A中成员变量大小为char k[3]为4字节,sizeof(B)为12
C中vptr=4+自己类中成员变量大小,加上B和A中成员变量大小,为16
如果是虚继承:public virtual
A中vptr=4+成员大小=8
B中vptr=4+B中成员大小(4)+sizeof(A)=16
C中vptr+C中成员大小+sizeof(B)=24
在c++中虚指针指的就是vptr,指向虚函数表的那个指针
在多重继承中,如果C继承A和B,如果出现相同的函数foo()如何明确子类用的哪个父类的foo()?
答:C.A::foo()和C.B::foo()来代表从A类中继承的foo函数和从B类中继承的foo函数
问:如果鸟是可以飞的,那么鸵鸟是鸟吗?鸵鸟如何继承鸟类?
答:可以采用组合的办法,在鸵鸟类中定义和鸟类公有的方法,比如在鸵鸟类的eat方法调用鸟类.eat()即可
在c++继承中注意的是calss Derive:Base{} 如果不指定public,默认的是私有继承,所以应该写继承的时候class Derive:public Base中public是必不可少的
c++的Shape类中virtual void Draw()=0; 加上=0表示它是个纯虚函数,该类不能实例化一个对象,Shape s;都是不可以的
32位机器用十六进制表示的话,那么4为表示一位,那么要有八位。
int有4字节,1个字节有8位,因为是16进制,所以在32位机器下4位二进制数表示1个16进制,因此32位机器,用16进制表示是8位,因为1字节是8位,用16进制表示是2位。
因此一个char类型的数在内存中表示使用2位表示。int类型4个字节,用8位表示。
例题:在32位x86平台下,char a[11] = {100,999,3,4,5,6,7,8,9}; int *p = (int*)a; 那么*p是什么,*(p+1)是什么
解析:
注意的是,在char数组中存放的是10进制的,但是在内存中存放的是16进制,我们需要将10进制转16进制,先必须转成2进制,4位看成一位转成16进制
上图是实际内存的存放,我们先来看下在char数组中存放的100是按照10进制,需要转成16进制,怎么转呢?
2的0次方=1, 2的1次方=2, 2的2次方=4, 2的3次方=8, 2的4次方16, 2的5次方为32, 2的6次方为 64.
100-64=36-32-4=0
因此
128 64 32 16 8 4 2 1
0 1 1 0 0 1 0 0
表示成二进制为0110 0100,转成16进制为需要把4位看做1位。0110=6 0100 =4 因此用16进制表示为64.
好了,因为a数组保存char类型,一个char为1字节,1字节8位二进制,16进制4位二进制表示1位,因此我们看到1个char中内存是用2位表示的
这题有个细节将char类型的指针转成了int指针。因为int类型是4字节。有8位,因此输出的*p是8位,p的起始地址为a的地址,
因此*p输出0x0403e764
p+1中p为int类型指针因此向后移动4字节
*(p+1)输出为0x08070605
做笔试题的时候一定要注意类型的转换,指针的类型
这里有个细节,我们看到a我分配了11个字节内存,实际我只是初始化了9个数据,那么后面2个内存使用00来保存的,告诉我们分配了多少空间都是按照00来保存,没有分配空间内存中显示cc
c++中STL容器
C++中有两种类型的容器:顺序容器和关联容器。
顺序容器主要有vector、list、deque等。其中vector表示一段连续的内存,基于数组实现,相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的.list表示非连续的内存,基于链表实现,deque与vector类似,但是对首元素提供插入和删除的双向支持。
关联容器主要有map和set。map是key-value形式,set是单值。map和set只能存放唯一的key,multimap和multiset可以存放多个相同的key。
容器类自动申请和释放内存,因此无需new和delete操作。
几种标准的非STL容器,包括数组、bitset、valarray、stack、queue和priority_queue。
一、vector
内部数据结构:数组。
vector<int>
vec1; //默认初始化,vec1为空
vec1.push_back(100); //添加元素
vec1.pop_back(); //删除末尾元素
二、list
List是stl实现的双向链表,与 向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢。需要添加头文件list
三、deque
内部数据结构:数组。
deque容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入数据:push_front()。其余类似vector操作方法的使用。
四、map
C++中map容器提供一个键值对(key/value)容器,Map会根据key自动排序。
五、set
set的含义是集合,它是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就像一个集合一样。所有的操作的都是严格在logn时间之内完成,效率非常高。set和multiset的区别是:set插入的元素不能相同,但是multiset可以相同。Set默认自动排序。使用方法类似list。
六 queue(队列)
可以使用数组也可以使用链表来实现。不能遍历整个queue
c++中防止头文件被重复包含的方法
第一种方法
有一个很方便的宏: #pragma once,加入#pragma once后,编译器在打开或读取第一个#include 模块后,就不会再打开或读取随后出现的相同#include 模块.
第二种方法
用条件编译语句来实现:
#ifndef "XX_H"
#define "XX_H"
<头文件定义正文>
#endif
实际应用:
有下面的自定义结构体,定义在sample.h中。
类A,类B都#include<sample.h>,主程序都调用了类A,类B;就会出现
error C2011: ”sample” : ”struct” type redefinition
解决方法:写上宏定义:
也可以这样写
if(宏sample_H_H,没有被定义过)
{
定义宏sample_H_H
……..(执行)other code
}
实际上sample_H_H作为一个标记而存在
自定义一个类时,在所有的头文件中都应用这组宏处理
注意是#ifndef不是#ifdef
#ifndef 标记名(常以类名_H_H,两个标记名要相同)
#define 标记名
//你原来的文件内容
#endif
memcpy的源码:
const char src[5] = "3333";
那么拷贝的时候,如果用memcpy1(dest,src,sizeof(src));则printf(dest);出来是3333
如果memcpy1(dest,src,4);则printf(dest);出来是33335666;因为上面的sizeof(src),包含'/0',所以拷贝过去的字符串以'/0'
结束,就只有3333,而如果传4个字符,'/0'是第五个字符,那就遇到dest[1024] 的'/0'结束,所以是33335666
字符串的'/0'问题一定要注意啊!!!
strcpy的源码:
1 传入的src防止被修改,所以要用const来修饰
2 代码的健壮性,当传入的指针是空的时候,要报错,这里用assert(断言),它的作用是判断一个表达式,如果结果为假,输出诊断消息并中止程序,也可以这样:
if ((strDest==NULL)||(strSrc==NULL)) //[1]
throw "Invalid argument(s)";
3 返回目标地址
忘记保存原始的dst值,如果不保存des的值,那么des++的时候地址已经改变了,因此返回des不是传入的des的地址
4 '\0'
循环写成while (*dst++=*src++);明显是错误的。
循环写成while (*src!='\0') *dst++=*src++;
循环体结束后,dst字符串的末尾没有正确地加上'\0'。
strcpy的内存重叠情况:
char s[10]="hello";
//strcpy(s+1, s); //应返回hhello,但实际会报错,因为dst与src重叠了,把'\0'覆盖了
因为s后一位的值是前一位,实际输出s为“hhhhh.....”,栈溢出报错,把\0都覆盖了,
所谓重叠,就是字符串中未处理部分被处理部分覆盖了,memcpy函数自带内存重叠检测功能,下面给出memcpy的实现my_memcpy。
可以看到,先判断输出的地址是否在输入字符串地址长度范围内,如果在,就从高往低向后进行复制
strcpy与memcpy的区别:
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
c++中32位struct的sizeof算法:
1,先计算有多大
2,最终为内部最大成员长度的整数倍
sizeof(AA)=48,因为加上bb结构体又是24所以总共48
重载:overload
覆盖:override
运行时绑定和编译时绑定:
编译时绑定(静态绑定):在程序执行前,编译器就知道要调用哪个方法
运行时绑定(动态绑定):在编译阶段不知道调用哪个方法,知道运行的时候才知道
重载,包括静态方法重载和普通方法重载,静态方法重载是通过类名.方法调用,因为该方法在类中声明为static所以是静态绑定,普通方法重载是new出对象,再对象.方法来实现,因为可能有多态,所以是动态绑定
覆盖,也即重写,子类覆写了父类的方法,有多态,肯定是动态绑定塞。
判断:指针p指向一个对象,则sizeof(p)的值是4,sizeof(*p)的值是p指向对象的实际大小
答案:
笔试题1
char s[]=“12345\t\n\0abcd\0”,求strlen[s] = 7, sizeof[s] = 14.
strlen 是函数,运行时才能确定,所以它只计算了12345\t\n
而sizeof 是操作符,它包括所有的字符12345\t\n\0abcd\0,还要加一个结束字符,所以为13+1 = 14,这里容易犯错,我以为是13没想到最后的\0后面还要加上\0
int anTest[5][10]; int n1=&anTest[4]-&anTest[0], n2=&anTest[3][1]-&anTest[1][3]; 则n1=____4___ ,n2= ______18____
这题一开始想的是160和17,结果答案为4和18,对于指针的相加减是加减后乘除该指针类型的大小,在二维数组中地址相加减是加减运算后乘除一个单位的大小,&anTest[0]表示横轴的10个数,单位是10个int类型,
所以&anTest[4] - &anTest[0]=(4字节int大小乘以10个元素乘以4个横轴)除以(4字节int大小乘以10个元素)=4
&anTest[4][0] - &anTest[0][0]=40
如图,D继承B和C,B和C同时继承A,那么图4中在类D中会出现两次A,为了节省空间,将B和C对A的继承定义为虚拟继承,A就成了虚拟基类
class A; class B::public virtual A; class C:public virtual A; class D :public B,public C;如果是子类虚继承父类,那么sizeof(子类)得到的值=子类的vptr指向自己的虚函数表的指针(4字节)+子类本来的成员所占空间的大小+sizeof(父类)
因此虚继承算sizeof(类)得到的结果和直接继承不一样,看清继承的时候是直接继承还是虚继承
例题一
class A{ char k[3]; public: virtual void aa(){}; }; class B :public virtual A{ char j[3]; public: virtual void bb(){}; }; class C :public virtual B{ char i[3]; public: virtual void cc(){}; };如果是直接继承,求sizeof(A),sizeof(B),sizeof(C)
如果是如上的虚继承,求sizeof(A),sizeof(B),sizeof(C)
答:
直接继承:public
A中char k[3]大小为3,做一次数据对齐后(编译器以4倍数为对齐单位)char k[3]所占大小为4,加上vptr指针,sizeof(A)为8
B中vptr为4字节,加上自己的char j[3]为4字节,加上A中成员变量大小为char k[3]为4字节,sizeof(B)为12
C中vptr=4+自己类中成员变量大小,加上B和A中成员变量大小,为16
如果是虚继承:public virtual
A中vptr=4+成员大小=8
B中vptr=4+B中成员大小(4)+sizeof(A)=16
C中vptr+C中成员大小+sizeof(B)=24
在c++中虚指针指的就是vptr,指向虚函数表的那个指针
在多重继承中,如果C继承A和B,如果出现相同的函数foo()如何明确子类用的哪个父类的foo()?
答:C.A::foo()和C.B::foo()来代表从A类中继承的foo函数和从B类中继承的foo函数
问:如果鸟是可以飞的,那么鸵鸟是鸟吗?鸵鸟如何继承鸟类?
答:可以采用组合的办法,在鸵鸟类中定义和鸟类公有的方法,比如在鸵鸟类的eat方法调用鸟类.eat()即可
在c++继承中注意的是calss Derive:Base{} 如果不指定public,默认的是私有继承,所以应该写继承的时候class Derive:public Base中public是必不可少的
c++的Shape类中virtual void Draw()=0; 加上=0表示它是个纯虚函数,该类不能实例化一个对象,Shape s;都是不可以的
32位机器用十六进制表示的话,那么4为表示一位,那么要有八位。
int有4字节,1个字节有8位,因为是16进制,所以在32位机器下4位二进制数表示1个16进制,因此32位机器,用16进制表示是8位,因为1字节是8位,用16进制表示是2位。
因此一个char类型的数在内存中表示使用2位表示。int类型4个字节,用8位表示。
例题:在32位x86平台下,char a[11] = {100,999,3,4,5,6,7,8,9}; int *p = (int*)a; 那么*p是什么,*(p+1)是什么
解析:
注意的是,在char数组中存放的是10进制的,但是在内存中存放的是16进制,我们需要将10进制转16进制,先必须转成2进制,4位看成一位转成16进制
上图是实际内存的存放,我们先来看下在char数组中存放的100是按照10进制,需要转成16进制,怎么转呢?
2的0次方=1, 2的1次方=2, 2的2次方=4, 2的3次方=8, 2的4次方16, 2的5次方为32, 2的6次方为 64.
100-64=36-32-4=0
因此
128 64 32 16 8 4 2 1
0 1 1 0 0 1 0 0
表示成二进制为0110 0100,转成16进制为需要把4位看做1位。0110=6 0100 =4 因此用16进制表示为64.
好了,因为a数组保存char类型,一个char为1字节,1字节8位二进制,16进制4位二进制表示1位,因此我们看到1个char中内存是用2位表示的
这题有个细节将char类型的指针转成了int指针。因为int类型是4字节。有8位,因此输出的*p是8位,p的起始地址为a的地址,
因此*p输出0x0403e764
p+1中p为int类型指针因此向后移动4字节
*(p+1)输出为0x08070605
做笔试题的时候一定要注意类型的转换,指针的类型
这里有个细节,我们看到a我分配了11个字节内存,实际我只是初始化了9个数据,那么后面2个内存使用00来保存的,告诉我们分配了多少空间都是按照00来保存,没有分配空间内存中显示cc
c++中STL容器
C++中有两种类型的容器:顺序容器和关联容器。
顺序容器主要有vector、list、deque等。其中vector表示一段连续的内存,基于数组实现,相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的.list表示非连续的内存,基于链表实现,deque与vector类似,但是对首元素提供插入和删除的双向支持。
关联容器主要有map和set。map是key-value形式,set是单值。map和set只能存放唯一的key,multimap和multiset可以存放多个相同的key。
容器类自动申请和释放内存,因此无需new和delete操作。
几种标准的非STL容器,包括数组、bitset、valarray、stack、queue和priority_queue。
一、vector
内部数据结构:数组。
vector<int>
vec1; //默认初始化,vec1为空
vec1.push_back(100); //添加元素
vec1.pop_back(); //删除末尾元素
二、list
List是stl实现的双向链表,与 向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢。需要添加头文件list
三、deque
内部数据结构:数组。
deque容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入数据:push_front()。其余类似vector操作方法的使用。
四、map
C++中map容器提供一个键值对(key/value)容器,Map会根据key自动排序。
五、set
set的含义是集合,它是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就像一个集合一样。所有的操作的都是严格在logn时间之内完成,效率非常高。set和multiset的区别是:set插入的元素不能相同,但是multiset可以相同。Set默认自动排序。使用方法类似list。
六 queue(队列)
可以使用数组也可以使用链表来实现。不能遍历整个queue
c++中防止头文件被重复包含的方法
第一种方法
有一个很方便的宏: #pragma once,加入#pragma once后,编译器在打开或读取第一个#include 模块后,就不会再打开或读取随后出现的相同#include 模块.
第二种方法
用条件编译语句来实现:
#ifndef "XX_H"
#define "XX_H"
<头文件定义正文>
#endif
实际应用:
有下面的自定义结构体,定义在sample.h中。
typedef struct sample{ int trueNumber; double feature[13]; }SAMPLE;
类A,类B都#include<sample.h>,主程序都调用了类A,类B;就会出现
error C2011: ”sample” : ”struct” type redefinition
解决方法:写上宏定义:
#ifndef sample_H_H #define sample_H_H typedef struct sample{ int trueClass; double feature[13]; }SAMPLE; #endif
也可以这样写
#if !define sample_H_H #define sample_H_H typedef struct sample{ int trueClass; double feature[13]; }SAMPLE; #endif意思是:
if(宏sample_H_H,没有被定义过)
{
定义宏sample_H_H
……..(执行)other code
}
实际上sample_H_H作为一个标记而存在
自定义一个类时,在所有的头文件中都应用这组宏处理
注意是#ifndef不是#ifdef
#ifndef 标记名(常以类名_H_H,两个标记名要相同)
#define 标记名
//你原来的文件内容
#endif
memcpy的源码:
void *memcpy1(void *desc,const void * src,size_t size) { if((desc == NULL) && (src == NULL)) { return NULL; } unsigned char *desc1 = (unsigned char*)desc; //将指针都转成char类型防止类型不匹配 unsigned char *src1 = (unsigned char*)src; while(size-- >0) { *desc1 = *src1; desc1++; src1++; } return desc; }char dest[1024] = "12345666";//{0};
const char src[5] = "3333";
那么拷贝的时候,如果用memcpy1(dest,src,sizeof(src));则printf(dest);出来是3333
如果memcpy1(dest,src,4);则printf(dest);出来是33335666;因为上面的sizeof(src),包含'/0',所以拷贝过去的字符串以'/0'
结束,就只有3333,而如果传4个字符,'/0'是第五个字符,那就遇到dest[1024] 的'/0'结束,所以是33335666
字符串的'/0'问题一定要注意啊!!!
strcpy的源码:
char * strcpy(char *dst,const char *src) //[1] { assert(dst != NULL && src != NULL); //[2] char *ret = dst; //[3] while ((*dst++=*src++)!='\0'); //[4] return ret; }这里注意以下几点:
1 传入的src防止被修改,所以要用const来修饰
2 代码的健壮性,当传入的指针是空的时候,要报错,这里用assert(断言),它的作用是判断一个表达式,如果结果为假,输出诊断消息并中止程序,也可以这样:
if ((strDest==NULL)||(strSrc==NULL)) //[1]
throw "Invalid argument(s)";
3 返回目标地址
忘记保存原始的dst值,如果不保存des的值,那么des++的时候地址已经改变了,因此返回des不是传入的des的地址
4 '\0'
循环写成while (*dst++=*src++);明显是错误的。
循环写成while (*src!='\0') *dst++=*src++;
循环体结束后,dst字符串的末尾没有正确地加上'\0'。
strcpy的内存重叠情况:
char s[10]="hello";
//strcpy(s+1, s); //应返回hhello,但实际会报错,因为dst与src重叠了,把'\0'覆盖了
因为s后一位的值是前一位,实际输出s为“hhhhh.....”,栈溢出报错,把\0都覆盖了,
所谓重叠,就是字符串中未处理部分被处理部分覆盖了,memcpy函数自带内存重叠检测功能,下面给出memcpy的实现my_memcpy。
char *my_memcpy(char *dst, const char* src, int cnt) { assert(dst != NULL && src != NULL); char *ret = dst; if (dst >= src && dst <= src+cnt-1) //内存重叠,从高地址开始复制 { dst = dst+cnt-1; src = src+cnt-1; while (cnt--) *dst-- = *src--; } else //正常情况,从低地址开始复制 { while (cnt--) *dst++ = *src++; } return ret; }此段摘自http://www.cnblogs.com/chenyg32/
可以看到,先判断输出的地址是否在输入字符串地址长度范围内,如果在,就从高往低向后进行复制
strcpy与memcpy的区别:
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
c++中32位struct的sizeof算法:
1,先计算有多大
2,最终为内部最大成员长度的整数倍
typedef struct bb { int id; double weight; float height; }BB; typedef struct aa { char name[2]; int id; double score; short grade; BB b; }AA;sizeof(BB)=24;
sizeof(AA)=48,因为加上bb结构体又是24所以总共48
重载:overload
覆盖:override
运行时绑定和编译时绑定:
编译时绑定(静态绑定):在程序执行前,编译器就知道要调用哪个方法
运行时绑定(动态绑定):在编译阶段不知道调用哪个方法,知道运行的时候才知道
重载,包括静态方法重载和普通方法重载,静态方法重载是通过类名.方法调用,因为该方法在类中声明为static所以是静态绑定,普通方法重载是new出对象,再对象.方法来实现,因为可能有多态,所以是动态绑定
覆盖,也即重写,子类覆写了父类的方法,有多态,肯定是动态绑定塞。
判断:指针p指向一个对象,则sizeof(p)的值是4,sizeof(*p)的值是p指向对象的实际大小
答案:
笔试题1
char s[]=“12345\t\n\0abcd\0”,求strlen[s] = 7, sizeof[s] = 14.
strlen 是函数,运行时才能确定,所以它只计算了12345\t\n
而sizeof 是操作符,它包括所有的字符12345\t\n\0abcd\0,还要加一个结束字符,所以为13+1 = 14,这里容易犯错,我以为是13没想到最后的\0后面还要加上\0
int anTest[5][10]; int n1=&anTest[4]-&anTest[0], n2=&anTest[3][1]-&anTest[1][3]; 则n1=____4___ ,n2= ______18____
这题一开始想的是160和17,结果答案为4和18,对于指针的相加减是加减后乘除该指针类型的大小,在二维数组中地址相加减是加减运算后乘除一个单位的大小,&anTest[0]表示横轴的10个数,单位是10个int类型,
所以&anTest[4] - &anTest[0]=(4字节int大小乘以10个元素乘以4个横轴)除以(4字节int大小乘以10个元素)=4
&anTest[4][0] - &anTest[0][0]=40
相关文章推荐
- C++基础知识的总结(主要针对面试)
- 自己总结C/C++的一些容易被遗忘的基础知识!
- C++基础知识总结与回顾--5.5共享数据的保护
- C/C++基础知识总结3
- C++基础知识总结
- C/C++语言基础知识总结
- C/C++基础知识总结——C++简单程序设计
- C++基础知识易错点总结(1)
- C++基础知识总结----类的进阶知识点
- C++基础知识总结
- C/C++基础及高频率面试知识总结
- C++基础知识: 公有继承,保护继承,私有继承的总结,私有继承的用意何在
- C/C++面试之基础知识总结篇
- C++基础知识易错点总结(1)
- c++学习历程(3)之 第二章基础知识总结
- C++基础知识总结
- C++ 顺序容器基础知识总结
- C++基础知识点总结之常量与引用
- c++基础知识总结
- (总结之mfc书最后附录)c++基础知识点-概念