第十一章-第十二章

 

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成员变量。

猜你喜欢

转载自blog.csdn.net/qq_23084801/article/details/81286635