1.
b.编译器为了CPU计算,作出的数据对齐处理(可用#pragma pack(n)来设定变量的对齐方式)。
c.为了支持虚函数,产生的额外负担。
//例子:
#pragma pack(2)
class BU
{
int number; //4
union UBffer
{
char buffer[13];
int number;
}ubuf; //13+1
void foo(){} //0
typedef char*(*f)(void*); //0
enum{hdd,ssd,blueray}disk; //4
}bu;
//sizeof(bu)的值为22。
出处:《C++Primer》中文版第五版541页第22行。
我们可以为纯虚函数提供定义,不过函数体必须定义在类的外部。若定义在类的内部,会出现错误:pure-specifier on function-definition。
class Dummy
{
//error:pure_specifier on function-definition.
virtual void process()=0{};
};
class Dummy
{
virtual void process()=0;
}
void Dummy::process()
{}
cast_name
A.static_cast:任何具有明确定义的类型转换,只要不包含底层const(比如常量指针,而不是指针常量),都可以使用。常用于窄化转换(告诉程序的读者和编译器:我们知道并且不在乎潜在的精度损失),编译器无法自动执行的类型转换(找回存在于void*指针中的值)。
a.用于类层次结构中基类和派生类之间指针或者引用的转换(up-casting把派生类的指针或引用转换成基类的指针或引用是安全的,down-casting把基类的指针或引用转换成派生类的指针或引用是不安全的)。
b.基本类型之间的转换。
c.把空指针转换成目标类型。
d.不能提供数字到指针的转换,不能提供不同类型指针之间的转换。
B.const_cast:只能改变运算对象的底层const(一旦我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的后果)。只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型。常用于函数重载中。
//比较两个string对象的长度,返回较短的那个引用.
const string &shortString(const string &s1,const string &s2)
{
return s1.size()<=s2.size()?s1:s2;
}
/*当我们对两个非常量的string实参调用这个函数,但返回的结果仍然是const string的引用。因此我们需要一种新的shortString函数,当它的实参不是常量时,得到的结果是一个普通的引用,使用const_cast可以做到这一点:*/
string &shortString(string &s1,string &s2)
{
//调用这个函数的目的:我们只要比较大小,这样调用是声明我们不能修改参数!!
auto &r=shortString(const_cast(s1),const_cast(s2));
return const_cast(r);
}
C.reinterpret_cast:通常为运算对象的位模式提供较低层次上的重新解释(数字到指针之间的转换,不同类型指针之间的转换)。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝,在类型之间指向的内容不做任何类型的检查和转换。慎用!!
D.dynamic_cast:该转换符用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用,注意dynamic_cast转换符只能用于含有虚函数的类。比如含有虚函数的基类B和从基类B派生出的派生类D,则B *pb; D *pd, d; pb=&d; pd=dynamic_cast
4.
a.text:已编译程序的机器代码。
b.data:已初始化的全局变量。
c.bss:未初始化的全局变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。
d.heap:
e.stack:
5.
A.通用多态
a.参数多态:模板。
b.包含多态:virtual。
B.特定多态
a.重载多态:
b.强制多态:强制类型转换。
6.
面向对象的三大基本特征:封装(数据抽象)、继承和多态(动态绑定)。
面向对象的五大基本原则:单一职责原则、开放封闭原则(扩展性开放,更改性封闭)、里氏替换原则、接口隔离原则和依赖倒置原则。
7.
8.
构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。不建议在构造函数和析构函数里面调用虚函数。
9.
val.operator++(); //前置++。
val.operator++(0); //后置++。
10.
enum中:首元素不赋值的话,默认为0。后一个元素不赋值的话比前一个元素大1。
11.
所有的虚函数都必须有定义。
12.
-:左对齐。
:::作用域操作符。当作用域操作符的左侧为空时,向全局作用域发出请求。
13.
C语言:char a='a'; sizeof(char)=1;sizeof(a)=1;sizeof('a')=4;
int a=3;
class A
{
public:
A():ra(a){}
private:
int &ra;
};
//在64位系统中,sizeof(A)为8!
1.
对于一个运算符函数来说,它或者是类的成员(拥有this,即有一个类类型的参数),或者至少含有一个类类型的参数。
int operator+(int,int); ////错误,不能为int重定义内置的运算符。
MyClass operator+(int,int); //不行。
MyClass operator+(int,MyClass); //可以。
MyClass operator+(MyClass,int); //可以。
MyClass operator+(MyClass,MyClass); //可以。
2.
当我们把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象。(出处:《C++Primer》中文版第五版493页第13行。)
如果我们想提供含有类对象的混合类型表达式,则运算符必须定义成非成员函数。唯一的要求是至少有一个运算对象是类类型。
//有如下类模板定义:
template
class BigNumber
{
int n;
public:
BigNumber(T i):n(i){}
BigNumber operator+(BigNumber b)
{
return BigNumber(n+b.n);
}
};
//已知b1,b2是BigNumber的两个对象,则下列表达式中错误的是?
//3+3
//b1+3
//b1+b2
//3+b2(错误!)
3.
//赋值运算符优先级比逗号运算符优先级高。注意下面两个表达式的区别:
a=b,c;
a=(b,c);
4.
//请问下面的程序一共输出多少个“-”?8个。
int main()
{
int i;
for(i = 0;i<2;i++)
{
fork();
printf("-");
}
return 0;
}
//一共调用了6次printf,但是会输出8个-。因为父进程的输出缓冲也会被子进程复制。
//因为标准输出是行缓冲,程序遇到"\n"、EOF、文件描述符关闭、主动flush或程序退出等,才会把数据刷出缓冲区。
5.
int fun(char str[14])
{
return sizeof(str); //考点:此时str是一个char指针。
}
6.
define a 10
void foo();
int main()
{
printf("%d ",a);
foo();
printf("%d",a);
return 0;
}
void foo()
{
#undef a //在这之前a都被替换成10
#define a 50 //从这开始a被替换成50
}
//输出10 10
#define a 10
void foo();
void prin();
int main()
{
prin();
printf("%d ",a);
foo();
printf("%d\n",a);
return 0;
}
void foo()
{
#undef a
#define a 50
}
void prin()
{
printf("%d ",a);
}
//输出50 10 10,可以看出define只是在预处理阶段将a替换为相应数值,具体替换的值只与define在文件中的位置有关,与是否在函数内无关。
7.
关于浮点数相等问题:通过比较两数相减的绝对值是否小于FLT_EPSILON/DBL_EPSILON/LDBL_EPSILON。
8.
int main()
{
int a=-3;
unsigned int b=2;
long c=a+b;
printf("%ld\n",c);
}
//c的结果为-1或4294967295。(跟long是32位还是64位有关)
9.
class A
{
public:
virtual void func(int val=1)
{
std::cout<<"A->"<"<func(); //B->1
p1->text(); //B->1
p2->func(); //B->0
p2->text(); //B->1(虽然p2的静态类型是B*,但是调用text()后,用A*调用func())
return 0;
}
重点:缺省参数值是静态绑定的,绝不重新定义继承而来的缺省参数值。