1,格式控制符问题
对于问题:
float k=0.8567;
printf("%06.1f%%",k*100);
最后的输出是什么?
从上面的输出控制符我们可以得知,从左往右我们有:
- 0:表示第一个非零数字前用零填充
- 6:表示用6个位置
- .1:表示小数点后保留一位小数,四舍五入
- %%:表示输出%
因此,最后的输出为:
0085.7%
2,fopen函数的用法
- 函数:fopen
- 函数原型:
FILE * fopen(const char * path,const char * mode);
- 返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回
NULL
,并把错误代码存在errno
中。
一般而言,打开文件后会做一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在fopen()
后作错误判断及处理。
参数说明:
参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
mode有下列几种形态字符串:
- “r” 以只读方式打开文件,该文件必须存在。
- “r+” 以可读写方式打开文件,该文件必须存在。
- ”rb+“ 读写打开一个二进制文件,允许读写数据(可以任意修改),文件必须存在。
- “w” 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
- “w+” 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
- “a” 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
- ”a+“ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
- “wb” 只写打开或新建一个二进制文件;只允许写数据(若文件存在则文件长度清为零,即该文件内容会消失)。
- “wb+” 读写打开或建立一个二进制文件,允许读和写(若文件存在则文件长度清为零,即该文件内容会消失)
- “wx” 创建文本文件,只允许写入数据.[C11]
- “wbx” 创建一个二进制文件,只允许写入数据.[C11]
- ”w+x“ 创建一个文本文件,允许读写.[C11]
- “wb+x” 创建一个二进制文件,允许读写.[C11]
- “w+bx” 和"wb+x"相同[C11]
- “rt” 只读打开一个文本文件,只允许读数据
- “wt” 只写打开或建立一个文本文件,只允许写数据
- “at” 追加打开一个文本文件,并在文件末尾写数据
- “rb” 只读打开一个二进制文件,只允许读数据
- “wb” 只写打开或建立一个二进制文件,只允许写数据
- “ab” 追加打开一个二进制文件,并在文件末尾写数据
- “rt+” 读写打开一个文本文件,允许读和写
- “wt+” 读写打开或建立一个文本文件,允许读写
- “at+” 读写打开一个文本文件,允许读,或在文件末追加数据
- “rb+” 读写打开一个二进制文件,允许读和写
- “ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据
其中,对于r+和w+:
相同点:
- 二者都是可读写
- 二者都从文件头开始写
- 二者都是当文件不存在时创建文件
不同点:
4. r+直接在原文上覆盖,未覆盖的字符保留
5. w+先将原文清零,再写入
3,运算符的重载
C++语言允许在重载运算符时改变运算符原来的功能。例如将“++
符号重载时,可以定义为--
的功能。但是,不提倡这样做,重载运算符最好仍保持原有的功能。
而,C++语言不允许重载运算符时改变:
- 运算符的操作个数
- 运算符的优先级
- 运算符的结合性
4,虚函数与指针问题
如果调用的函数是实函数,则看指针的定义;如果调用的函数是虚函数,则看指针的指向(赋值)。
对于问题:
#include<iostream>
using namespace std;
class Base
{
int x;
public:
Base(int b): x(b) {}
virtual void display()
{
cout << x;
};
};
class Derived: public Base
{
int y;
public:
Derived(int d): Base(d), y(d) {} void display()
{
cout << y;
}
};
int main()
{
Base b(1);
Derived d(2);
Base *p = & d;
b.display();
d.display();
p->display();
return 0;
}
最后的输出为?
- b为Base类的对象,调用显示x,为1;
- d为派生类对象,b.display()自然调用派生类的函数,显示y,为2;
p->display()
为基类指针指向派生类对象,(此为多态的实现核心,详情可参考虚表部分内容),此时调用的函数由对象决定,对象是派生类对象,故调用派生类的函数,故输出y,为2
因此,最后的输出为122。
根据上面的原则,指针p调用的是虚函数,而指针p的指向(赋值)为派生类,所以是派生类的函数部分。
5,数组作为函数实参的问题
- 在主调函数中,不一定非要指定数组的大小。比如,如果是char[] 数组,我们可以通过最后的’\0’来识别数组结束,不一定必须要显式说明数组大小。
- 在被调函数中,不需要考虑形参数组的大小。因为传数组都是只传一个地址过来。
6,static与const
- 对于static,用其修饰的变量不可以被其他的文件使用。
- 数据段可以分成data段——初始化不为0的static和global数据、以及bss段——未初始化或者已初始化为0的static和global数据。
- static静态变量存储在静态域中。
- const表面含义是个常量,但实际上还是占据一个内存位置的变量,但是它的值一般实在编译时期就决定了。
7,静态成员
- 非静态成员函数也可以操作静态数据成员。
- 静态成员是属于类的,不是属于对象的,而this是当前对象的指针,所以静态成员没有this指针。
- 在C++中,类的静态成员(static member)必须在类内声明,在类外初始化,像下面这样:
class A
{
private:
static int count ; // 类内声明
};
int A::count = 0 ; // 类外初始化,不必再加static关键字
为什么?因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。
而在类内可以初始化的成员只有一种,就是静态常量成员,其余类型的数据成员均不可以在类内初始化。
- 错误一:
class A
{
private:
static int count = 0; // 静态成员不能在类内初始化
};
- 错误二:
class A
{
private:
const int count = 0; // 常量成员也不能在类内初始化
};
- 错误三:
class A
{
private:
static const float count = 3.14; // 非静态整型成员常量不能在类中初始化
};
- 正确写法
class A
{
private:
static const int count = 0; // 静态整型常量成员可以在类内初始化
};
8,纯虚函数
纯虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
不过值得注意的是,我们也可以为纯虚函数提供定义,不过函数体必须定义在类的外部。
9,构造函数的执行顺序
当派生类中不含对象成员时:
- 在创建派生类对象时,构造函数的执行顺序是:基类的构造函数 → 派生类的构造函数;
- 在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数 → 基类的析构函数。
当派生类中含有对象成员时:
- 在定义派生类对象时,构造函数的执行顺序:基类的构造函数 → 对象成员的构造函数 → 派生类的构造函数;
- 在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数 → 对象成员的析构函数 → 基类的析构函数。
10,引用类型
对于引用类型,在使用时必须要设定初值、进行初始化;指针倒是不用。