程序基础
- 负号优先级高于后++,即-i++为 -i,后i++
- ++i返回对象的引用,i++返回对象的值,自定义数据类型的时候,++i效率更高
- bool类型用if(flag)和if(!flag)
- 当表达式中存在有符号类型和无符号类型时,所有的操作数都会自动转换成无符号类型
- 不使用任何中间变量的a b值交换,如下,推荐3,2会发生溢出
1:
a=a+b;
b=a-b;
a=a-b;
2:
a^=b;
b^=a;
a^=b;
- C++和C有什么不同?在于解决问题的思想不一样,相对于原来的C有所加强,引入了重载、内联函数、异常处理等,更是扩展了面向对象设计的内容,如类、继承、虚函数、模板和包容器等
- 标准头文件如下, 防止头文件被重复引用,extern “C”包含双重含义:
- 其声明的函数和变量可以在本模块和其他模块中使用
- 是按照C语言方式编译的
#ifndef _c
#define _c
#ifdef _cplusplus
extern "C"{
#endif
...
#ifdef _cplusplus
}
#endif
#endif
- #include<>表明文件是工程或标准头文件,会首先检查预定义的目录,#include”“表明该文件是用户提供的头文件,从当前文件目录中寻找,然后在标准位置寻找文件
- C++main函数执行完后还执行其他函数么?
int atexit(void (*)(void));。用atexit()注册函数,这些处理函数的调用顺序与注册的顺序相反,在main结束后执行
预处理,const,static, sizeof
预处理,const
- 使用#define 注意吧参数小心地用括号括起来
- const在声明同时一定要初始化
- const和define区别:
- const常量存在于数据段,有分配的空间,run-time,有数据类型,会类型安全检查
- define compile-time,声明周期止于编译器,没有实际的存在
- const作用?
- 定义常量
- 修饰函数形式参数,如const &a
- 修饰函数返回值
- 修饰类成员函数
static
- static的作用
- 在函数体,被调用过程中维持其值不变
- 在模块内函数外,一个静态变量可以被所有模块内其他函数调用,要先声明
- 模块内,只可以被这一摸块内的其他函数调用
- static局部变量和全局变量和函数区别
- 把局部变量static后,改变了存储方式,改变了生存期(整个运行期),只初始化一次
- 把全局变量static后,限制了使用范围,改变了作用域
- static函数在内存中只有一份,而普通函数在每个调用中维持一份复制品
sizeof
计算普通变量
- int字节数跟语言无关,跟机器位长相同,32位机器是4字节,64位机器是8字节。数据类型的字节数应该是由CPU决定的,但是实际上主要由编译器决定(占多少位由编译器在编译期间说了算),TC下,2个字节,WINNT是4个字节
- sizeof指针是4(32位)或者8(64位)
类对象
- 结构体或者类对象(字节对齐)
- 首地址被其最宽基本类型成员大小整除(注意是基本类型成员)
- 相对于首地址偏移量都是成员大小的整数倍,要不填充字节
- 总大小为最宽基本类型成员大小的整数倍,要不填充字节(注意是基本类型成员)
- 对于类对象,不算静态成员,不算函数 如
struct s{
char c1;
int 1;
char c2;
}
sizeof: 12
含有虚函数的类对象
- c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。
- 普通对象不占用内存,只要有虚函数,就会占用一个指针大小的内存。一个隐含的虚表指针成员,并且,无论多少个虚函数都不会再影响类的大小
- 多继承会有多个虚函数表,就多个指针
虚拟继承的类对象大小
- 空类1个字节
- 静态变量在静态存储区,不计入大小
- 每一个虚继承,编译器会为该类安插一个指向父类的指针,多个虚拟继承,就多个指向父类的指针
其他
- sizeof和strlen区别
- sizeof是操作符,strlen是函数
- sizeof是size_t,unsigned int
- sizeof可以用类型做参数,strlen只用char*,以\0结尾
- sizeof接变量可以不加括弧,类型加括弧
- sizeof计算联合体 union对齐与struct一样,按最大的来,char[]和int ,按int对齐,取整数,如
union{ char a[13];int a;}是最接近13的整数16 - #pragma pack(n)将对齐设置为n,编译器默认是8,所以每个成员使用自身的对齐
- 内联函数:取代表达式形式的宏定义,有参数类型检查,有类型的返回值等,最主要用法是定义存取函数,宏在预编译展开,而内联函数在编译时展开
- 不能把所有函数都定义为内联函数,赋值开销大
引用和指针
- 不存在NULL的引用,不能改变为另一个对象的引用,只能初始化时指向一次
- char* 存放在数据区,相同内容,会相同指向,同时,是常量对str[0]修改
- 野指针不是NULL指针,而是指向垃圾内存的指针,在free或者delete后,没有置为NULL,让人误以为是个合法的指针,需要置为NULL
- 为什么需要new/delete,既然有了mallloc/free? malloc无法满足动态对象的要求,malloc是库函数不是运算符,不能把执行构造函数和析构函数任务强加于malloc/free,用malloc创建对象不会执行构造和析构
- 各种内存分配和释放函数的联系和区别
- malloc: (type*)malloc(size) 分配长度为size的连续区域,返回首地址,没初始化,是随机数
- calloc: (type*)calloc(n,size) 分配n块长度为size的连续区域,返回首地址,初始化为0
- realloc: (type)realloc(ptr,size),增加ptr内存到size, 增加的没有初始化
- free(void *ptr)析构
- 内存分配方式有几种:
- 静态存储区
- 栈上
- 堆上
- 句柄和指针的区别,句柄是一种指向指针的指针
- 句柄所指的是一个很复杂的结构,比如线程的句柄,与系统交互
- 指针也可以指向用户定义的复杂结构,由用户完成
字符串
编程实现strcpy函数
char *strcpy(char *strDest,const char* strSrc){//copy the string
if((strDest==NULL)||(strSrc==NULL)){
return NULL;
}
char *strDestCopy = strDest;
while((*strDest++=*strSrc++)!='\0');
return strDestCopy;
}
编程实现memcpy函数
void *memcpy2(void *memTo,const void *memFrom,size_t size){
assert((memTo!=NULL)&&(memFrom!=NULL));
char *tempFrom=(char *)memFrom;//保存首地址
char *tempTo=(char*)memTo;//保存首地址
while(size-->0){
*tempTo++=*tempFrom++;
}
return memTo;
}
其他
- strcpy和memcpy区别
- 复制内容不同,strcpy只能字符串,后者任意内容
- 前者不需要制定长度,\0结尾,后者需要第三个参数
- strlen实现
int strlen(const char *src){
assert(NULL!=src);
int len=0;
while(*str!='\0'){
len++;
}
return len;
}
- strcmp实现
int mystrcmp(const char *str1,const char *str2)
{
for( int i = 0; ; i < MAX, *(str1+i) != '\0',
*(str2+i) != '\0'; i++){
if(*(str1+i) == *(str2+i))
continue; //相等时继续比较下一位
return (*(str1+i) > *(str2+i))?1:(-1);
}
return 0;
}
- 关键字volatile: 说明变量会意想不到的改变,每次重新读取这个变量的值而不是使用保存在寄存器里的备份
C++面向对象
- class和struct区别
- class默认访问权限和继承方式是private
- struct….是public
- struct不可应用模板类型
- 当类中含有const、引用成员变量和基类的构造函数时都需要初始化列表
- 空类默认函数
class Empty{
public:
Empty();
Empty(const Empty&);//复制
~Empty();
Empty& operator=(const Empty&);//赋值
Empty* operator&();//取址
}
- 析构函数不能被重载
- explict构造函数不能被隐式调用
- 如果继承关系为私有,编译器不会将派生类对象转换成基类对象
- 多继承的构造函数顺序
- 虚拟基类按照被继承的顺序
- 非虚拟基类按照被继承的顺序
- 成员对象按照声明顺序
- 类自身构造函数
- 抽象类=虚基类
- COM?
- 组件对象模型
- 二进制级共享代码,任何语言都可以用,如DLL,动态链接库