1. 继承关系
子类只能继承父类的protected 和 public成员。private不可继承。私有继承后父类成员就作为子类的私有成员,无法继续向下继承。私有继承(class Derived: private Base)子类可以访问父类成员,但是之类的实例不能。C++默认继承是private继承。
2 虚函数和虚继承
每个虚函数都在vtable中占了一个表项,保存着一条跳转到它的入口地址的指令。当一个包含虚地址的对象(不是对象的指针)被创建时,它在头部附加了一个指针,指向vtable中相应的位置。调用虚函数的时候,不管你用什么指针调用的,它先根据vtable 找到入口地址再执行,从而实现“动态联编”。而不像普通函数那样简单地跳转到一个固定地址。
虚继承用于解决菱形继承问题:B, C都继承了A, 然后D继承B,C那么D中存在 两个A。采用虚继承只保留一个A但是会增加 虚表指针。非虚继承每个实例只有一个虚指针而且该指针指向自己的虚表。
class A
{
char k[3];
public:
virtual void aa(){ };
};
class B : public virtual A
{
char j[3];
public:
virtual void bb(){ };
};
class C : public virtual B
{
char i[3];
public:
virtual void cc(){ };
};
int main()
{
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
cout << sizeof(C) << endl;
return 0;
}
/*
虚继承:8, 16, 24
如果不是虚继承:8, 12, 16
*/
为什么虚函数效率低?
一般函数在可以在编译时定位到函数的地址, 虚函数(动态调用)要根据某个指针定位到函数地址,多加了一个过程。
dynamic_cast<Drivid*>(p_base);dynamic_cast用于将父类指针反向替换为子类指针(改指针之前使用多态由子类转换为父类,才能成功)
纯虚函数
virtual void Draw() = 0;
RTTI:Runtime Type Information, RTTI包括dynamic_cast和typeid(typeid()返回一个typeinfo对象,能确定对象的动态类型)。
3. 操作符重载
如果运算符被定义为全局函数,对于一元运算符是一个参数,对于二元运算符是两个参数。如果运算符是 成员函数,对于一元运算符没有参数,对于二元运算符是一个参数。
class A
{
private:
int a;
public:
A() {a=0;}
void operate++() //++a
{
a += 1;
}
void operate++(int)//a++
{
a +=1;
}
}
int main()
{
A class_a;
++class_a;
class_a++;
return 0;
}
4、位运算与嵌入式编程
5为int型,32位平台为4字节,因此在stack中分配4字节内存存放参数5. printf根据“%f”认为是个double型(printf中float会自动转换为double),因此从stack中读取8个字节
int main()
{
printf("%f\n", 5);
printf("%d\n", 5.01);
return 0;
}
/*
0.000000
1889785610
*/
结构体位制概念
struct a{
int x:1;
int y:3;
int x:12;
};
C++中类型转换
ANSI-C++标准定义了四个新的转换符:'reinterpret_cast', 'static_cast', 'dynamic_cast' 和 'const_cast',目的在于控制类(class)之间的类型转换。
reinterpret_cast<new_type>(expression)
操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。
static_cast<new_type>(expression)
在编译时使用类型信息进行转换,在转换执行必要的检测,其操作数相对安全。不能把struct转换成int
const_cast<new_type>(expression)
转换掉对象的const属性
dynamic_cast<new_type>(expression)
子类指针利用虚函数转换到父类后,用dynamic_cast可以再转换回子类。不是这种情况则转换失败返回NULL
class A
{
public:
virtual void foo() {cout << "A foo"<<endl;}
void pp() {cout <<"A pp" <<endl;}
};
class B:public A
{
public:
void foo() {cout << "B foo"<<endl;}
void pp() {cout << "B PP"<<endl;}
void FunctionB(){cout << "Excute FunctionB!"<<endl;}
};
int main()
{
A a;
A *pa=&a;
pa->foo();
pa->pp();
(dynamic_cast <B*>(pa))->FunctionB();
(dynamic_cast <B*>(pa))->foo();
(dynamic_cast <B*>(pa))->pp();
/*这部分代码等价于
B *pb = NULL;
pb->FunctionB();
pb->foo();
pb->pp();
*/
return 0;
}
FunctionB()和pp()未使用任何成员数据,也不是虚函数,不需要this指针,也不需要动态绑定,可以正常 运行。foo因为调用了虚函数,编译器 需要根据对象的虚函数指针查找虚函数表,此时为 空,为违法访问。
很多编译开发 厂商提供一种扩展---让标准C支持中断。其代表为interrupt
int void f()
{
}
ISR(中断子程序)不能返回值,不能传递参数, 不允许做浮点运算(浮点运算一般不可重入)。
volatile说明一个变量可能被意想不到的改变,这样编译器不会假设这个值。优化器在用这个 变量时必须每次都小心的读取这个变量的值,而不是使用保存在寄存器里的备份。一个参数可以既是 const又是volatile,如只读状态寄存器,它是volatile, 因为它可能被意想不到的改变;它 又是cosnt,因为程序不应该试图去修改它。
嵌入式 访问寄存器
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
补码
十进制-10的二进制8位补码为 2^8 - |-10|= 246 = 11110110
对于4位3进制补码 = 3^4 - |-10| = 71 = 2212
static作用
1、 static变量内存只被分配一次,因此在下次调用时仍维持上次的值。
2、 在模块内static全局变量可以被模块所有函数访问,但不能被模块外其他函数访问。
3、 在 类中static成员 变量属于整个类,对类的所有对象只有一份复制。
4、在类中的static成员函数属于整个类,这个函数不接收this指针,因而只能访问类的static成员变量。