题目:已知表达式++a中的”++”是作为成员函数重载的运算符,则与++a等效的运算符函数调用形式为:
a.operator++();
要点分析:
a.operator++();
a.operator++(0);
题目:以下程序的运行结果是()
int main(void)
{
printf("%s , % 5.3s\n","computer","computer");
return 0;
}
要点分析:
1. %m.ns 表示输出占m列,但只取字符串中左端n个字符。这n个字符输出在m列的右侧,左补空格。
2. 格式化输出如果规定了输出列的,不够的时候是右对齐,左侧补零。够了就全部输出。
答案:computer,com
题目:假设在一个 32 位 little endian (小端模式) 的机器上运行下面的程序,结果是多少? ( 1,0,2)
#include
int main()
{
long long a = 1, b = 2, c = 3;
printf("%d %d %d\n", a, b, c);
return 0;
}
要点分析:
- long在32位机器是4字节,在64位机器是8字节,但是long long,不管在哪个机器都是8字节;(后续再博客里会整理的)
- printf函数的原型是printf(const char*,…);
- printf函数是自右向左一股脑压入栈的;
- %d输出的是4个字节,int类型所占的字节数。
有了上述四条,本题目就相对比较简单了。由于a,b,c
是long long
类型,占8个字节,因此:
问题: math.h的abs返回值( )?
要点分析:
c中的函数申明为 int abs(int num);
int取值范围是负数比正数多1的,因此:
1. num为0或正数时,函数返回num值;
2. 当num为负数且不是最小的负数时,函数返回num的对应绝对值数,即将内存中该二进制位的符号位取反,并把后面数值位取反加一;
3. 当num为最小的负数时,由于正数里int类型表示不了这个数的绝对值,所以依然返回该负数。
详细内容见其他人的博客带你玩转Visual Studio——调用约定__cdecl、__stdcall和__fastcall
这里仅仅只是拿过来做整理之用。
要点 | __cdecl | __stdcall | __fastcall |
---|---|---|---|
参数传递方式 | 右->左 | 右->左 | 左边开始的两个不大于4字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数自右向左压栈传送 |
清理栈方法 | 调用者清理 | 被调用函数清理 | 被调用函数清理 |
适用场合 | C/C++、MFC的默认方式; 可变参数的时候使用; | Win API | 要求速度快 |
C编译修饰约定 | _functionname | _functionname@number | @functionname@number |
定义理解:
- 类型安全如果两个类型直接要相互转换,必须要显示的转换,不能偷偷摸摸的只用一个等于号就隐式转换了;
- 类型安全的类就是指别的类要转换为此类的时候需要调用此类提供的方法进行显示转换
题目:
判断下述语句的对错:MFC中CString是类型安全的类(对)
分析:
MFC数据类型转换标准库std的string 和MFC类库CString之间通过CString的format方法进行转换。
题目:
在一台主流配置的PC机上,调用f(35)所需的时间大概是__。(几分钟)
int f(int x) {
int s=0;
while(x-- >0)
s+=f(x);
return max(s,1);
}
这道题实质上是考时间复杂度的。由于递归是在循环里的,所以其时间复杂度:
O(f(35))=O(f(34))+O(f(33))+…+O(f(0))= 235 2 35 O(f(0))。现在的计算机大致每秒运算一亿次。所以大约花去 235 2 35 /100000000m大致为344秒也就是几分钟。
其他:
malloc 和 free:是管理虚拟内存;所以当然可以通过malloc(size_t)函数调用申请超过该机器物理内存大小的内存块。
同理,无法通过内存释放函数free(void*)直接将某块已经使用完的物理内存直接还给操作系统。(此外,可能是程序结束才还给操作系统)。
题目:以下定义错误的是:a)
a) struct A{A _a;};
b) struct A{A * _a;};
c) struct A{A & _a;};
d) struct B;
struct A{B & _b;};
struct B{A & _a;};
题目要点分析:
1. 结构体在完成定义之前是incomplete type(不完全类型),不完全类型不能定义对象,只能定义引用和指针,或者用于声明函数的形参和返回值类型。
2. struct成员类型不可以是它自己。
否则会导致递归定义。 理论上这样导致结构体的大小不能被计算(无限大小)。
但是成员可以定义为该结构体的指针(引用在c++底层是用指针实现的,所以也可以),因为指针的大小是已知的。
以下内容取自《c++ premier第五版》P540-542页。
将虚函数声明语句分号之前书写=0就可以将虚函数定义为纯虚函数。
上述=0 只能出现在类内部的虚函数声明语句处。
含有(或未经覆盖直接继承)的纯虚函数的类是抽象基类。且我们不能直接为抽象基类创建对象。
派生类必须覆盖纯虚函数给出自己的定义,否则它们仍然是抽象基类。
派生类的构造函数只能初始化其直接基类。仍然需要调用抽象基类自己的构造函数来初始化其成员。
纯虚函数可以有函数体,但是定义必须在类的外部。
虚函数可以是另一个类的友元函数,但不能是静态成员函数:
友元函数不能是虚函数,因为友元函数不可以继承.
在基类中声明了虚函数,在派生类中如果不声明,则默认为虚函数。(因此,不一定在派生类中显示声明。)
c++继承和组合的区别
总结:
1. 组合也叫“对象持有”,就是在类中定义另一类型的成员。组合拥有良好的扩展性,支持动态组合,因此请优先考虑组合方法;
2. 继承会破坏类的独立性,增加系统的复杂性,一般系统的继承层次不超过3层。
组合和继承优缺点:
组 合 关 系 | 继 承 关 系 |
---|---|
优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 | 缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性 |
优点:具有较好的可扩展性 | 缺点:支持扩展,但是往往以增加系统结构的复杂度为代价 |
优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 | 缺点:不支持动态继承。在运行时,子类无法选择不同的父类 |
优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 | 缺点:子类不能改变父类的接口,但子类可以覆盖父类的接口 |
缺点:整体类不能自动获得和局部类同样的接口 | 优点:子类能自动继承父类的接口 |
缺点:创建整体类的对象时,需要创建所有局部类的对象 | 优点:创建子类的对象时,无须创建父类的对象 |
题目:下列关于异常处理的描述中,理解不正确的是:
1. C++语言的异常处理机制通过3个保留字throw、try和catch实现。
2. 任何需要检测的语句必须在try语句块中执行,并由throw语句抛出异常。
3. throw语句抛出异常后,catch利用数据类型匹配进行异常捕获。
4. 一旦catch捕获异常,不能将异常用throw语句再次抛出。(错误)
catch异常后,同样可以用throw语句在此抛出,如果没有解决,会直接向上层继续抛,直至main函数,然后异常停止!
这是个老生常谈的问题,但是没办法我还是记不太清楚。因此这里特别再整理以下:
题目:请声明一个指针,其所指向的内存地址不能改变,但内存中的值可以被改变:
const int const *x = &y;
int * const x = &y;
const int *x = &y;
int const *x = &y;
const int * const x = &y;
分析:
1. 题目的意思是不难的,指向内存地址不变,但内容可以变,意味着只是一个顶层const。
2. 形容指针本身地址时不能改变的就是要把const放在“*”号的后面。
3. 如果要形容指针指向的地址内存放的内容不可以改变,就是要const放在“*”号的前面。
4. const的位置与int等关键词的位置并没有什么特别的要求。
综上,答案应该是int * const x = &y;