类的 继承

注意:给形参默认值:从左往右的顺序依次给,中间不能有空的

class Employee
{
	char name[20];
	Date birthday;
public:
	Employee(char* a, int b, int c, int d):birthday(b, c, d)
	{
		strcpy_s(name, a);
	}

};

参数初始化表的使用:

构造函数中初始化组合、继承时

兼容类型规则:

在这里插入图片描述

派生类的对象可以当作基类对象
这时只能使用基类成员

#include<iostream>

using namespace std;

class base {

public:

    void fun() { cout << "base::fun()" << endl; }

};

class derive :public base {

public:

    void fun() { cout << "derive::fun()" << endl; }

};

int main() {

    base* p;

    base b;

    derive d;

    p = &b;

    p->fun();

    p = &d;

    p->fun();

    return 0;

}

在这里插入图片描述
在这里使用一个基类指针,指向派生类的对象,却只能访问基类的同名函数。这一点在后文:同名隐藏也会提到。

访问控制规则

e.g. 私有继承下public和protected都变成了private,在派生类中可以访问,在类的对象中(类外)不可访问(后续会持续出现这一概念,不要混淆)
private在任何继承情况下都变成不可访问的,在派生类中都不可以访问

#include<iostream>
using namespace std;
class base {
private:
protected:
	int num = 0;
public:
	int sum = 0;
	void fun() { cout << "base::fun()" << endl; }
};
class derive :private base {
public:
	void showbase() { cout << num <<' '<<sum<< endl; }
	void fun() { cout << "derive::fun()" << endl; }
};
int main() 
{
	derive d;
	d.showbase();
}

输出:0 0

间接调用基类成员函数

法1:
不能使用基类函数,在派生类中写同名函数,并定义为调用基类的函数(类外不能使用继承来的private,但是类内可以)

class A
{
public:
	void f1() {
		cout << "b f1" << endl;
	}
protected://在类外不可访问(相当于是private)在类内相当于是public
	void f2()
	{
		cout << "bf2" << endl;
	}
};
class B:public A {
public:
	void f1() { A::f1(); cout << "Af1" << endl; }
	void f2() { A::f2(); }
};
int main()
{
	B a;
	a.f1();
	A* p = &a;
	p->f1();
}

在这里插入图片描述
同样的,基类指针只能调用基类的同名函数。这里需要和后文虚函数区分。
法2:
对象在调用的时候说明是哪个类的同名函数

#include<iostream>
using namespace std;
class A
{
public:
	void f1() {
		cout << "af1" << endl;
	}
public:
	void f2()
	{
		cout << "af2" << endl;
	}
};
class B :public A {
public:
	void f1() { A::f1(); cout << "Bf1" << endl; }//执行顺序:先输出再调用A的f1
	void f2() { A::f2(); cout << "Bf2" << endl; }
};
int main()
{
	B a;
	a.f1();
	A* p = &a;
	p->f1();
	a.A::f2();
}

在这里插入图片描述
先执行完本函数,再去调用其他类的函数。

单一继承的构造函数和析构函数

基类的构造函数和析构函数都不能被继承
在这里插入图片描述

#include<iostream>
using namespace std;
class A
{
public:
	A() { cout << "con A" << endl; }
	~A() { cout << "des A" << endl; }
};
class B :public A {
	A a;//组合
public:
	B() { cout << "con B" << endl; }
	~B() { cout << "des B" << endl; }
};
int main()
{
	B b;
}

con A
con A
con B
des B
des A
des A

调用顺序:
先构造基类(如果在构造函数中没写,调用对应类的默认构造函数)
再构造派生类中的数据成员,
最后执行派生类对象的构造函数中的语句。
注意
派生类的构造函数在没有写调用基类构造函数的情况下,运行时仍然需要调用基类的构造函数。
可以说,只要建立派生类对象,就需要调用基类构造函数。

但是析构函数:
向上文的程序中的main函数中添加:

	A* p = new B();

输出:
con A
con A
con B
con A
con A
con B
des B
des A
des A

添加:

A* p = new B();
	delete p;

输出
con A
con A
con B
con A
con A
con B
des A
des B
des A
des A

可以发现:基类指针即使是在指向派生类对象后,在delete的时候也只能调用基类的析构函数,导致派生类的数据会有剩余,这就需要后文的虚析构函数。
析构函数调用顺序和类的组合一样,与构造函数的顺序正好相反

区分 类的组合

#include<iostream>
using namespace std;
class A
{
	int a;
public:
	A() {cout << "consA" << endl; }
	~A() { cout << "desA" << endl; }
	//void showa() { cout << a << endl; }
};
class B
{
	A a;
public:
	B()
	{
		cout << "consB" << endl;
	}
	~B() { cout << "desB" << endl; }
	//void showb() { cout << b << endl; }
};
int main()
{
	B a;
}

在这里插入图片描述
构造的调用顺序和函数编写顺序无关,只与数据成员声明顺序有关

#include<iostream>
using namespace std;
class A
{
	int a;
public:
	A() {cout << "consA" << endl; }
	~A() { cout << "desA" << endl; }
	//void showa() { cout << a << endl; }
};
class A2
{
public:
	A2() { cout << "consA2" << endl;}
	~A2() { cout << "desA2" << endl; }
};
class B
{
	A a;
	A2 b;
public:
	B():b(),a()//调用顺序与函数编写顺序无关
	{
		cout << "consB" << endl;
	}
	~B() { cout << "desB" << endl; }
	//void showb() { cout << b << endl; }
};
int main()
{
	B a;
}

析构和调用顺序相反

  • 综上
  • 总的构造函数调用顺序:
  • 先继承,再组合,都看声明时侯的顺序。
  • 析构函数
  • 先组合,再继承,与声明的顺序反着来

练习
(1)组合和继承混用时
在这里插入图片描述

#include <iostream>

using namespace std;

class A

{

public:

	A() { cout << "A"; }

	~A() { cout << "~A"; }

};

class B : public A

{

	A p;

public:

	B() { cout << "B"; }

	~B() { cout << "~B"; }

};

int main()

{

	B obj;

	return 0;

}

在这里插入图片描述
先基类再组合

#include <iostream>

using namespace std;
class C

{
public:

	C() { cout << "C"; }
	~C() { cout << "~C"; }

};

class A

{
	int n;
public:

	A() { cout << "A"; }
	~A() { cout << "~A"; }

};

class B : public A

{

	C p;

public:

	B() { cout << "B"; }

	~B() { cout << "~B"; }

};

int main()

{

	B obj;

	return 0;

}

(2)组合(指针形式),继承,指定的析构函数顺序

#include <iostream>

using namespace std;

class A

{

public:

	A() { cout << "A"; }

	~A() { cout << "~A"; }

};

class B : public A

{

	A* p;

public:

	B() { cout << "B"; p = new A(); }

	~B() { cout << "~B"; delete p; }

};

int main()

{

	B obj;

	return 0;

}

在这里插入图片描述
(3)派生类作为形参时


#include <iostream>

using namespace std;

class Base {

public:

	Base() { cout << "Base" << endl; }

	Base(Base& b) { cout << "copy base" << endl; }

	~Base() { cout << "base destroyed" << endl; }

};

class Derived : public Base {

public:

	Derived() { cout << "Derived" << endl; }

	Derived(Derived& d) {

		cout << "copy Derived" << endl;

	}

	~Derived() {

		cout << "Derived destroyed" << endl;

	}

};

void fun(Derived d)//注意,传递的时候,形参也需要构造函数哟
{

	cout << "fun called" << endl;

}

void main()

{

	{

		Derived d;

		fun(d);

	}

}

在这里插入图片描述
(4)基类指针开辟派生类的空间

#include <iostream>

using namespace std;

class A {

	int a;

public:

	A() { cout << "A default cons\n"; }

	A(int n) { cout << "A cons " << n << "\n"; }

	virtual ~A() { cout << "A des\n"; }

};

class B : public A {

public:

	B() { cout << "B default cons\n"; }

	B(int n) :A(n) { cout << "B cons " << n << "\n"; }

	virtual ~B() { cout << "B des\n"; }

};

int main()

{

	A* p = new B(10);

	delete p;

	return 0;

}

在这里插入图片描述
空间是什么类型的,相应的构造析构函数都需要使用

拷贝构造函数

#include<iostream>
using namespace std;
class A
{
	int a;
public:
	A(int i=0):a(i){}
	A(const A& m) { a = m.a; }
	void showa() { cout<<a; }
	~A() { cout << "desA" << endl; }
	//void showa() { cout << a << endl; }
};
class B:public A
{
	int b;
public:
	B(int i, int j) :A(i), b(j) {}
	B(const B& m):A(m),b(m.b) {//拷贝构造函数
	}
	void showb() { A::showa();cout<<b; }
	~B() { cout << "desB" << endl; }
	//void showb() { cout << b << endl; }
};
int main()
{
	B a(1, 2);
	B b(a);
	b.showb();
}

同名隐藏规则

#include <iostream>

using namespace std;

//---------------------------------

class Counter {

	int value;

public:

	Counter();

	Counter(int value);

	~Counter();

};//---------------------------------

class Example {

	int value;

public:

	Counter car;//表明Counter作为基类

	Example();

	Example(int val);

	void disp();

	~Example();

};//---------------------------------

Counter::Counter() {

	cout << "Counter Constructor1" << endl;

	value = 0;

}//---------------------------------

Counter::Counter(int val) {

	cout << "Counter Constructor2" << endl;

	value = val;

}//---------------------------------

Counter::~Counter() {

	cout << "Counter Destructor" << endl;

}//---------------------------------

Example::Example() {

	cout << "Example Constructor1" << endl;

	value = 0;

}//---------------------------------

Example::Example(int val) {

	cout << "Example Constructor2" << endl;

	value = val;

}//---------------------------------

void Example::disp() {

	cout << "value=" << value << endl;

}//---------------------------------

Example::~Example() {

	cout << "Example Destructor" << endl;

}//---------------------------------

int main() {

	Example e(2);

	e.disp();

}

在这里插入图片描述

#include <iostream>
using namespace std;
class B1
{ public:
int nV;
void fun() {cout<<"Member of B1"<<endl;} 
void fun(int n) {cout<<"Member of B1"<<endl;}
};
class D1: public B1
{ public:
int nV; //同名数据成员,派生类定义一个和基类同名成员,叫做覆盖
void fun(){cout<<"Member of D1"<<endl;}
}; //同名函数成员void main()
{ D1 d1;
//对象名.成员名标识, 访问D1类成员(若未强行指名,则通过派生类对象使用的是派生类中的同名成员))
d1.nV=1;
d1.fun();
//作用域限定符标识, 访问基类B1成员
d1.B1::nV=2;
d1.B1::fun();
d1.fun(10); 
//基类同名函数全被隐藏,要访问基类同名成员就需要像上面一样,用基类名限定
}
#include <iostream>
using namespace std;
class B1
{
public:
	int nV;
	void fun() { cout << "Member of B1 with 1" << endl; }
	void fun(int n) { cout << "Member of B1 with 2" << endl; }
};
class D1 : public B1
{
public:
	int nV; //同名数据成员
	void fun() { cout << "Member of D1" << endl; }
}; //同名函数成员void main()

int main()
{
	D1 d1;
	//对象名.成员名标识, 访问D1类成员
	d1.nV = 1;
	d1.fun();
	//作用域限定符标识, 访问基类B1成员
	d1.B1::nV = 2;
	d1.B1::fun();
	d1.fun(10);//不可行
	d1.B1::fun(10);//可行
	//基类同名函数全被隐藏
}

二义性

第一种:派生类继承两个基类

#include <iostream>
using namespace std;
class A
{
public:
	void f();
};
class B
{
public:
	void f();
	void g();
};
class C:public A,public B
{
public:

};

int main()
{
	C c;
	c.f();
	c.f(1);//二义性:第一种情况
}

第二种:派生类继承的几个基类由一个基类继承而来

class A
{
public:
	void f();
};
class B:public A
{
public:
	void g();
};
class C:public A
{
public:

};
class D:public C,public B
{
public:
};

相当于D的对象有两个f(),在通过D类访问时出现二义性
解决办法:
1)同名隐藏:声明同名函数,定义中写清楚调用哪个
2)写清类名进行约束
3)虚基类

虚基类

主要用来解决多继承时可能发生的对同一基类继承多
次而产生的二义性问题. – 为最远的派生类提供唯一的基类成员,而不重复产生
多次拷贝

#include <iostream>
using namespace std;
class B0 //声明基类B0
{
public:
	int nV;
	void fun() { cout << "Member of B0" << endl; }
};
class B1 : virtual public B0
{
public:
	int nV1;
};
class B2 : virtual public B0
{
public:
	int nV2;
}; 
class D1 : public B1, public B2 //派生类D1声明
{
public:
	int nVd;
	void fund() { cout << "Member of D1" << endl; }
};
void main()
{
	D1 d1; //声明D1类对象d1
	d1.nV = 2; //使用最远基类成员
	d1.fun();
}

含有虚基类的构造函数

#include <iostream>
using namespace std;
class B0
{ public:
B0(int n){ nV=n; cout << "cons";}
int nV;
void fun(){cout<<"Member of B0"<<endl;}
};
class B1: virtual public B0
{ public:
B1(int a) : B0(a) {}
int nV1;
};
class B2: virtual public B0
{ public:
B2(int a) : B0(a) {}
int nV2;
};class D1: public B1, public B2
{
public:
D1(int a) : B0(a), B1(a), B2(a){}
int nVd;
void fund(){cout<<"Member of D1"<<endl;}
};
void main()
{
D1 d1(1);
d1.nV=2;
d1.fun();
}

**只有最远派生类对于最远基类的构造函数有效。**B0的nv的初始化只初始化了一次,其余对于nv的初始化被忽略。所以最远派生类一定对于所有基类在构造函数中都要构造,否则使用基类的构造函数,且在中间件派生类中对于最远基类的构造被忽略。
在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,该派生类的其它基类对虚基类构造函数的调用被忽略。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值