【每日一练】---- 3.07 3.08 3.09oj总结(继承、重载、多态、动态与静态绑定)

  1. 重载父类派生类成员函数
    看如下代码,判断输出结果:
class A {
public:
	void test(int a) { cout << "1"; }
};
class B :public A {
public:
	void test(float b) { cout << "2"; }
};
int main() {
	A* a = new A;
	B* b = new B;
	a = b;
	b->test(2);
	a->test(1.1);
	return 0;
}

下面对这段代码解读:
这里考的是函数重载,在类B中,隐藏了继承下类A的成员函数,用类B的指针或引用去调用test函数,打印的结果都是“2”。当用类A的指针或者引用指向类B时,C++中有一种切片功能,当父类指针或引用指向派生类时,会切掉派生类的部分,保留父类的部分。故a调用test时打印的结果是“1”。最后打印的结果是“21”。
在这里插入图片描述

切不能被参数类型迷惑,记住父类成员函数与派生类成员函数构成重载时,派生类中会将父类的进行隐藏;父类指针或引用调用派生类对象时,具有切片功能。

  1. 多态
    看如下代码,判断输出结果:
class Base {
public:
	Base(int j) : i(j) {}
	virtual~Base() {}
	void func1() {
		i *= 10;
		func2();
	}
	int getValue() {
		return i;
	}
protected:
	virtual void func2() {
		i++;
	}
protected:
	int i;
};
class Child : public Base {
public:
	Child(int j) : Base(j) {}
	void func1() {
		i *= 100;
		func2();
	}
protected:
	void func2() {
		i += 2;
	}
};
int main() {
	Base* pb = new Child(1);
	pb->func1();
	cout << pb->getValue() << endl; delete pb;
}

首先在创建Child对象时,将父类Base中的i赋值为了1。用父类的指针去调用func1,而func1不是虚函数,所以构成重载,又根据切片功能,所以调用Base的fun1函数,接着调用func2函数,而func2函数是虚函数,且在派生类中完成了重写覆盖,故调用派生类Child的func2函数,i的值为12

在这里插入图片描述

谨记构成虚函数重写的条件

  1. 函数名参数类型个数均相同
  2. 父类中该函数必须有virtual修饰
  1. 虚继承
    看如下代码:
class A {
public:
	A(const char* s) { cout << s << endl; } ~A() {}
};
class B : virtual public A {
public: 
	B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class C : virtual public A {
public: 
	C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; }
};
class D : public B, public C {
public: 
	D(const char* s1, const char* s2, const char* s3, const char* s4) :B(s1, s2), C(s1, s3), A(s1) { cout << s4 << endl; }
};
int main() {
	D* p = new D("class A", "class B", "class C", "class D"); delete p; return 0;
}

在这里先大致解释一下虚继承:当出现这种菱形继承时,为了防止数据冗余,往往采用虚继承。意思就是当类D继承类B与类C时,D中只包括一份类A的数据,不会出现两份类A的数据,这就是虚继承。
弄清楚虚继承后,当构造D对象时,先从最最最基础的类A开始构造,而后按照B与C的继承先后顺序构造,即A->B->C->D
在这里插入图片描述

如果我们改变一下B与C的继承顺序
**

  1. 理解多态的动态绑定
    看一段代码:
class A {
public:
	void foo() { printf("foo"); }
	A() { bar(); }
protected:
	virtual void bar() { printf("bar"); }
};
class B :public A {
public:
	void foo() { printf("b_foo"); }
protected:
	void bar() { printf("b_bar"); }
};
int main() {
	A* p = new B;

	return 0;
}

分析代码: 首先构造出派生类B对象出来,此时调用B的构造函数与A的构造函数,A的构造函数中又调用函数bar() ,按照前面两题的思路函数bar()是虚函数且在派生类中完成了重写,所以调用B中的bar()会打印出“b_bar”,但是这是错误的,因为编译器在绑定虚函数时是动态绑定,此时类A正在实例化,类B还没有完成实例化,类B中的bar()函数自然没有完成重写,所以虚函数表中的指针仍是指向类A的bar(),所以打印“bar”。

  1. 动态绑定:运行时绑定
  2. 静态绑定:编译时就已经确定
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值