大学三年,接触最多的编程语言应该就是C和C++了,以下是本人对这两种编程语言一些经典问题的总结。这些问题在各司的笔试面试题中也常常以各种形式出现。
1.C/C++区别
(1) C是面向过程的,C++是面向对象的。
(2) C语言必须在函数开始处声明所有的变量。C++可以在任意的位置声明变量。
(3) C和C++对结构体的声明存在不同。
(4) C++中有bool型变量,C语言没有。
(5) C语言的输入输出采用printf(),scanf()等库函数。
C++的输入输出采用cin,cout流。
(6) C语言的动态内存分配采用malloc,free等函数。
C++采用new delete等操作符。
(7) C++中可以用const变量声明一个常量。
C++语言中想声明一个常量采用#define。
(8) C语言中没有引用。
(9) C语言中不能在for循环开始处定义变量,C++则可以。
2.const常量和define宏的区别:
(1) const常量有数据类型,宏常量则没有数据类型。编译器可以对const变量进行类型安全检查。但是对define宏只进行字符串替换。
(2) 同样,利用调试工具可以对const常量进行调试。3.sizeof问题
(1)
class A1
{
public:
int a;
static int b; //静态变量放在全局数据区,sizeof是计算栈中内存分布
};
sizeof(A1)=4
(2)
class A2
{
double d;
float a;
int b;
char c;
};
sizeof(A2)=24 // 8字节对齐
(3)
char *ss =”0123456789”
sizeof(ss)=4; //指针的大小
sizeof(*ss)=1; //计算的是一个字符的大小
strlen(ss)=10 //不包括’\0’
(4)
char ss[100]=”0123456789”
sizeof(ss)=100;
strlen(ss)=10;
(5)
int ss[100]=”0123456789”;
sizeof(ss)=400;
strlen(ss) //错误,仅用于char*
tips:
C++中涉及到继承问题时:
空类所占空间为1,
单继承占空间为1,
多继承占空间1,
虚继承设计虚指针,占大小4。
4.malloc/free 和new/delete区别:
(1)malloc/free是库函数,不在编译器的控制权限之内,new/delete是运算符,在编译器控制权限之内。
(2)对于非内部数据类型,new/delete调用时会自动调用构造/析构函数。
5.关于C++的虚析构函数
例如CChild继承自CBase。执行下述代码时:
CBase *pBase;
CChild c;
pBase=&c; //将基类的指针指向派生类对象的引用
如果父类的析构函数为非虚的,那么调用delete pBase时,调用的仅仅是CBase的析构函数。如果父类的析构函数为虚析构函数,那么调用delete pBase时,会先调用CChild的析构函数,再调用CBase的析构函数。
tips:
(1) 如果所有对象都是在栈中分配,那么对象都会被自动撤销,不会导致内存泄露。
如果有对象在堆中分配,而析构函数又不是虚的,那么可能会导致内存泄露。
(2) 如果将一个类的析构函数声明为虚析构函数,那么我们实际上认为该类应该作为父类来使用。如果该类不被继承,那么没有必要声明虚析构函数。因为虚函数的对象必须要维护一个虚函数表,增加了系统的开销。
(3) 不能将构造函数声明为虚的。因为在执行构造函数时类对象还没有完成建立过程,所以谈不上函数与类对象的绑定。
(4)虚函数机制:为包含虚函数的类建立一张虚拟函数表vtable在这些虚拟函数表中,编译器将依次按照函数声明次序放置类的特定虚函数的地址。同时在每个带有虚函数的类中放置一个vpointer指针,这个指针指向这个类的vtable。 每个类只能有一个虚拟函数表,如果没有虚拟函数,则不存在虚拟函数表。编译器的内联函数对虚函数调用无能为力。Virtual意味着等运行是决定调用哪个函数,inline意味着在编译期间将调用之处用被调函数替代。
6.重载和重写
重写(override) 派生类重写基类的虚函数,重写的函数要有一致的参数和返回值,实现了动态的多态性。
重载(overload) 参数不同,实现了静态的多态性。
多态性的两种描述:
具有不同功能的函数可以用同一个函数名。
向不同的对象发同一个消息,不同对象有不同行为。
静态多态性:(函数重载和运算符重载)指在程序编译时就能决定调用哪个函数,编译时多态性。
动态多态性:在程序运行过程中才能动态确定操作所针对的对象。运行时多态性。通过虚函数和继承实现。
函数重载处理的是同一层次上的同名函数问题,而虚函数处理的是不同派生层次上的同名函数问题。
前者是横向重载,后者可以理解为纵向重载。
与重载不同的是: 同一类族的虚函数的首部是相同的,而函数重载时函数的首部是不同的(参数个数、类型或序列不同)。
7.C++中虚基类和虚函数的区别
虚基类(就是虚继承)是为了解决多重继承对象冗余而出现的。
虚函数则是为了实现动态的多态性。
8.C++中构造函数和析构函数C++中构造函数和析构函数不能被继承。
构造函数执行顺序:基类->派生类中对象成员->派生类
析构函数执行顺序:派生类->基类
多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。按基类在被继承时所声明的次序从左到右依次调用,与它们在派生类的构造函数实现中的初始化列表出现的次序无关。
9.单目运算符的重载:
++A;
成员函数重载
A::operator++() A.operator++()
友元函数:
friend operator++(A&) operator++(A)
A++;
成员函数
A::operator++(int); A.operator++(0)
友元函数
friend operator++(A&,int) operator++(A,0)
10.标准C方式访问文件:
r 读文件,文件必须存在。
w 写文件,文件不是必须存在,如果文件已经存在会清空其内容。如果文件不存在则自动创建文件。
a 追加文件,文件不是必须存在,如果文件已经存在则附加在文件原来内容之后。如果文件不存在则自动创建。
r+ 读写模式,文件必须存在。
w+ 读写模式,如果文件已经存在则会清空其内容。
a+ 读/追加模式。
t 文本方式读写。
b 二进制方式读写。
11.open和fopen区别
open unix系统调用,返回一个文件句柄。文件读写不带缓存,直接读写磁盘数据。
fopen C标准函数库,返回一个文件指针。文件读写带缓存。
12. 位运算
与&运算
一个数&1可以判断一个正数的奇偶性。
或 |运算
一个数|1就是把二进制最末位变为1.如果要把最末位变为0,那么对这个数|1后再减1就可以。
^运算
^运算的逆运算是本身,两次异或同一个数结果不变。
常见的二进制位操作:
去最后一位 x>>1
在最后加一个0 x<<1
在最后加一个1 (x<<1)+1
最后一位变为1 x|1
最后一位变为0 x|1-1
最后一位取反 x^1
把右数第K位变成1 x|(1<<(k-1))
右数第k位变为0 x&~(1<<(k-1))
右边第k位取反 x^(1<<(k-1))
取末三位 x&7
取末k位 x&((1<<k)-1)
取右数第k位 x>>(k-1)&1
末k位变为1 x|((1<<k)-1)
末k位取反 x^((1<<k)-1)
去掉正数最右边的1 x&(x-1)
把右边连续的1变成0 x&(x+1)
右起第一个0变成1 x|(x+1)
右边连续0变成1 x|(x-1)
去掉右起第一个1的左边 x&(x^(x-1))
取右边连续的1 (x^(x+1))>>1
与&运算
一个数&1可以判断一个正数的奇偶性。
或 |运算
一个数|1就是把二进制最末位变为1.如果要把最末位变为0,那么对这个数|1后再减1就可以。
^运算
^运算的逆运算是本身,两次异或同一个数结果不变。
常见的二进制位操作:
去最后一位 x>>1
在最后加一个0 x<<1
在最后加一个1 (x<<1)+1
最后一位变为1 x|1
最后一位变为0 x|1-1
最后一位取反 x^1
把右数第K位变成1 x|(1<<(k-1))
右数第k位变为0 x&~(1<<(k-1))
右边第k位取反 x^(1<<(k-1))
取末三位 x&7
取末k位 x&((1<<k)-1)
取右数第k位 x>>(k-1)&1
末k位变为1 x|((1<<k)-1)
末k位取反 x^((1<<k)-1)
去掉正数最右边的1 x&(x-1)
把右边连续的1变成0 x&(x+1)
右起第一个0变成1 x|(x+1)
右边连续0变成1 x|(x-1)
去掉右起第一个1的左边 x&(x^(x-1))
取右边连续的1 (x^(x+1))>>1将第n位置位或清零
#define BIT (1<<n)
置位: a|=BIT
清零 a&=~BIT
一个正数对齐到n a=(a+n-1)&~(n-1)
13.C运算符优先级记忆方法
扩建点 () -> .
单算易比较 单目(++) 算术 移位 比较
胃饥三等点 位运算 逻辑 三目 赋值 逗号
易错点:
a<<8+n表示的含义是a<<(8+n)。
14. static关键字
(1)修饰全局变量,表示该变量的作用域只存在于本文件中,其他文件不能引用。
(2)当static修饰函数内的局部变量时,表示该变量存储在静态存储区,而不是存储在栈上。因此该变量具有记忆功能,函数每次执行后,它的值不会丢失。
(3)static修饰函数, C中static修饰函数表示这个变量仅在本文件中有意义,不会影响其他模块,其他文件中的函数不能调用此函数。
(4)C++中static修饰类中函数和变量,表示该函数和变量属于该C++类的静态成员,为所有的对象所共有。静态成员函数不接受隐含的this自变量,所以无法访问自己类的非静态成员。
15.inline函数和宏
宏是在预处理的地方把代码展开,不需要额外的空间和时间的开销,所以调用一个宏比调用一个函数高效。但是宏容易产生二义性,也不能访问对象的私有成员,这是宏的局限。
内联函数利用了宏的优点克服了宏的不足。宏是由预处理器对宏进行替代,而内联函数是由编译器控制实现的。内联函数像宏一样展开,取消了函数的压栈操作,减少调用开销。同时可以像调用函数一样调用内联函数,不必担心宏造成的问题。
16.C++类的默认形式
class Nothing
{
};
等价于:
class Nothing
{
public:
Nothing();
Nothing(constNothing &n);
Nothing&operator=(const Nothing &r);
Nothing*operator&();
const Nothing*operator &()const;
};
17.C++成员初始化原则:
(1)内置类型可以在初始化列表或函数体内初始化。
非内置类型应该在初始化列表初始化。(2)const常量必须在初始化列表初始化。(const成员只能被初始化,不能被赋值。)
(3)成员初始化顺序与声明的顺序一致。
(4)C++类的静态成员不能在类定义里边初始化 只能在类外初始化。
C/C++笔记(1)
最新推荐文章于 2022-03-11 14:40:25 发布