c++~第7课-----继承和派生

目录

继承方式与权限问题

继承中的构造函数

 继承中同名问题

菱形继承


继承方式与权限问题

  • 继承的写法:
  • 父类也称为基类;子类也称为派生类
  • 继承的实质:父类的数据和成员子类中有一份
  • 权限问题:继承方式只会增强父类属性在子类中的权限显示
  • 接口:
     //接口
        int& getNum()
        {
            return c;
        }
class 子类 :继承方式 父类

#include <iostream>
#include <string>
using namespace std;
//父类
class parent
{
public:
	int a;
    //接口
    int& getNum()
    {
        return c;
    }
protected:
	int b;
private:
	int c;
};
//公有继承
class son1 :public parent
{
public:
	void func()
	{
		a = 10;//父类中的公共权限成员,到子类中依然是 公共权限
		b = 20;//父类中的保护权限成员,到子类中依然是 保护权限
		//c = 30;//父类中的私有权限成员,子类访问不到

	}
};
//保护继承
class son2 :protected parent
{
public:
	void func()
	{
		a = 100;//父类中的公共权限成员,到子类中是 保护权限
		b = 200;//父类中的保护权限成员,到子类中依然是 保护权限
		//c = 30;//父类中的私有权限成员,子类访问不到
        //但是可以通过接口访问私有
        cout<<getNum()<<endl;
	}
};
//私有继承
class son3 :private parent
{
public:
	void func()
	{
		a = 1000;//父类中的公共权限成员,到子类中是 私有权限
		b = 2000;//父类中的保护权限成员,到子类中是 私有权限
		//c = 30;//父类中的私有权限成员,子类访问不到
	}
};
//孙子
class GrandSon :public son3
{
public:
	void func()
	{
		//a = 10000;//到了son3中 a,b变成私有,即使是儿子,也是访问不到
		//b = 20000;
	}
};
void test1()
{
	son1 s1;
	s1.a = 10;
	//s1.b = 20;//到son1中b是保护权限  类外访问不到
}
void test2()
{
	son2 s2;
	//s2.a = 10;//到son2中a是保护权限  类外访问不到
	//s1.b = 20;//到son2中b是保护权限  类外访问不到
}
void test3()
{
	son3 s3;
	//s3.a = 10;//到son2中a是私有权限  类外访问不到
	//s3.b = 20;//到son2中b是私有权限  类外访问不到
}
int main()
{
	return 0;
}
  • 派生类中的成员,包含两大部分:①一类是从基类继承过来的,一类是自己增加的成员②从基类继承过来的表现其共性,而新增的成员体现了其个性
  • 继承的好处:减少重复代码
  • 父类所有非静态成员属性都会被子类继承下去,父类中私有成员属性是被编译器隐藏了因此访问不到,但是确实被继承下去了

①打开“VS 2019的开发人员命令提示符”(“选择Developer Command Prompt for VS2019”)工具②输入"cd "文件所在路径③输入"dir"④输入"cl /d1 reportSingleClassLayout子类名 .cpp文件名"就可以查看了

class A
{
public:
	int a;
protected:
	int b;
private:
	int c;
};
class B : public  A
{
public:

protected:
	int d;
};

cout << sizeof(B) << endl;//输出结果:16
#include <iostream>
#include <string>
using namespace std;
//父类
class Common
{
public:
	void header()
	{
		cout << "首页,公开课,登陆,注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心,交流合作,站内地图...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java,Python,C++,...(公共分类列表)" << endl;
	}
};
class Java :public Common
{
public:
	void content()
	{
		cout << "Java学科视频"<<endl;
	}
};
class Python :public Common
{
public:
	void content()
	{
		cout << "Python学科视频" << endl;
	}
};
void test1()
{
	cout << "Java下载视频页面如下:" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
}
void test2()
{
	cout << "Python下载视频页面如下:" << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();
}
int main()
{
	test1();
	test2();
	return 0;
}

继承中的构造函数

  • 父类的属性通过父类的构造函数初始化
  • 子类中的构造函数,必须要调用父类构造函数,必须采用初始化参数列表的方式
  • 单继承和多继承

单继承:只有一个父类                        多继承:两个或者两个以上的父类

  • 继承的属性,无论被继承多少次,一直都存在,所以类一般不会被继承很多层,会导致类的臃肿
  • 继承中的构造和析构顺序

单继承:先构造父类,再构造子类,析构的顺序与构造顺序相反

多继承:构造顺序和继承顺序一致,析构相反;任何构造顺序问题都和初始化参数列表无关

//继承的属性一直都在
class A
{
public:
	A(int a):a(a){}
	int a;
};

class B:public A
{
public:
	B(int a,int b) :A(a),b(b){}
	int b;
};

class C:public B
{
public:
	C(int a,int b,int c):B(a,b),c(c) {}
	int c;
};

class D :public C
{
public:
	D(int a, int b, int c,int d) :C(a,b,c), d(d) {}
	int d;
};
//单继承
#include <iostream>
#include <string>
using namespace std;
//父类
class Parent
{
public:
	Parent() { cout << "父类无参构造函数" << endl; }
	Parent(string Fname,string Sname):Fname(Fname),Sname(Sname){}

	
protected:
	string Fname;
	string Sname;
};
//子类
class Son :public Parent
{
public:
	Son() { cout << "子类无参构造函数" << endl; } //指针写法,父类必须存在无参的构造函数,缺省也可以
	Son(string Fname, string Sname, string sonSname):Parent(Fname,Sname)
	{
		//自己的属性用什么办法初始化都行
		this->sonFname = Fname;
		this->sonSname = sonSname;
	}
	void print()
	{
		cout << "父亲:" << Fname + Sname << endl;
		cout << "儿子:" << sonFname + sonSname << endl;
	}
protected:
	string sonFname;
	string sonSname;
};

int main()
{
	Son s1;//子类构造对象,优先调用父类构造函数
	Son son1("李", "狗", "猪");
	son1.print();
	return 0;
}

//多继承
#include <iostream>
#include <string>
using namespace std;
//父类1
class MM
{
public:
	MM() = default;
	MM(string mmFname, string mmSname)
	{
		this->mmFname = mmFname;
		this->mmSname = mmSname;
	}
protected:
	string mmFname;
	string mmSname;
};
//父类2
class GG
{
public:
	//GG() = default;
	GG(string ggFname, string ggSname)
	{
		this->ggFname = ggFname;
		this->ggSname = ggSname;
	}
protected:
	string ggFname;
	string ggSname;
};
//子类
class Girl:public MM,public GG
{
public:
	//Girl(){}//需要两个父类都有无参构造函数
	//如果没有默认默认构造函数,需要用参数列表初始化,有则不需要
	Girl(string mmFname, string mmSname, string ggFname, string ggSname)
		:MM(mmFname,mmSname),GG(ggFname,ggSname)
	{
		girlFname = mmFname + ggFname;
		girlSname = mmSname + ggSname;
	}
	void print()
	{
		cout << "MM名字:" << mmFname + mmSname << endl;
		cout << "GG名字:" << ggFname + ggSname << endl;
		cout << "Girl名字:" << girlFname + girlSname << endl;
	}
protected:
	string girlFname;
	string girlSname;

};
int main()
{
	Girl k("洋", "子", "欧", "文");
	k.print();
	
	return 0;
}

 继承中同名问题

  • 数据成员同名
  • 成员函数同名

①子类对象可以直接访问到子类中同名成员

②子类对象加作用域可以访问到父类同名成员

③当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

  • 静态成员处理方式和非静态处理方式一样,只不过有两种访问方式:(通过对象和通过类名)
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
	MM(string name,int age):name(name),age(age){}
	void print()
	{
		cout << "父类打印" << endl;
	}

protected:
	string name;
	int age;
};

class Girl :public MM
{
public:
	Girl(string name,int age):MM("父类",88),name(name), age(age) {}
	void print()
	{
		//不做特别处理,就近原则
		cout << name << " " << age << endl;
		//用类名限定,就可以打印父类的数据
		cout << MM::name << " " << MM::age << endl;
		MM::print();//函数也是一样不做特别处理也是就近原则,所以要加类名限定
	}

protected:
	string name;
	int age;
};

int main()
{
	//正常对象调用
	Girl m("子类",44);
	m.print();
	MM mm("mm", 11);
	mm.print();

	//正常指针调用
	//就近原则
	Girl* pG = new Girl("newGirl", 23);
	pG->print();
	pG->MM::print();//这样也可以
	MM* mG = new MM("newMM", 32);
	mG->print();

	//非正常的指针
	//允许子类对象初始化父类指针
	MM* pMM = new Girl("newGirl", 999);
	//那pMM的print是MM类还是Girl?
	//在没有virtual情况下,看指针类型,有则看赋值对象
	pMM->print();

	//父类对象初始化子类指针,不安全
	//Girl* pGG = new MM("newMM", 5550);//错误
	
	//引发中断
	//Girl* pgg = (Girl*)mG;
	//pgg->print();
	return 0;
}

菱形继承

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  • 利用虚继承可以解决菱形继承问题

#include <iostream>
#include <string>
using namespace std;
class A
{
public:
	A(int a):a(a){}

protected:
	int a;
};
class B :virtual public  A
{
public:
	B(int a,int b):A(a),b(b){}
	void print2()
	{
		cout << a << endl;
	}
protected:
	int b;
};
class C :virtual public  A
{
public:
	C(int a, int c) :A(a),c(c){}
protected:
	int c;
};
class D :public  B,public C
{
public:
	//菱形继承,必须调用祖父的构造函数
	D() :B(1, 2), C(3, 4), A(9) {}//和B类,C类无关
	void print()
	{
		//只有一份,所以打印都是一样的
		cout << a << endl;
		cout << B::a << endl;
		cout << C::a << endl;
		print2();//间接访问也是一样的
	}
protected:
	
};
int main()
{
	D dd;
	dd.print();
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
1. 虚函数是可以[New一个对象的时候要根据虚函数的函数体来填虚表;而内联函数没有函数体,只是在预编译阶段展开]内联的,这样就可以减少函数调用的开销,提高效率(错误) 2. 一个类里可以同时存在[同一个类里无论什么函数都不能函数名和参数完全一样]参数和函数名都相同的虚函数与静态函数(错误) 3. 父类的析构函数是非虚的,但是子类的析构函数是虚的,delete子类指针(指向该子类对象)[特殊情况,参见题5],会调用父类的析构函数(正确)//任何情况下删除子类都会调用到父类的析构函数 4.对于下面的类CA,sizeof(CA) = _B_: A. 4 B. 8 C. 12 D. 16 class CA { public: CA(); virtual ~CA(); //因为有虚函数,所以会有4个字节的虚表指针 private: int m_iTime; //成员变量4个字节 public: int GetTime(); int SetTime(int iTime); }; 5.下面这段程序,打印结果是_A_: A. 1 B. 2 C. 3 D. 以上都不对 int g_iCount = 0; class CParent { public: CParent() {} ~CParent() {g_iCount += 1;} }; class CSon : public CParent { public: CSon() {} ~CSon() {g_iCount += 2;} }; main() { CParent* p = new CSon(); delete p[由于p被声明成父类指针,并且父类和子类的析构函数都非虚,因此delete操作只能根据p指针声明的类型来调用父类的析构函数]; std::cout << g_iCount << std::endl; } 6.请问下面这段程序的输出结果是_A_: A. 2,1, B. 2,2, C. 1,1, D. 1,2, class CParent { public: CParent() {} virtual ~CParent() {} public: virtual void Print() { std::cout << "1,"; }; }; class CSon : public CParent { public: CSon() {}; virtual ~CSon() {}; public: void Print() { std::cout << "2,"; }; }; void Test1(CParent& oParent[这里是引用了一个外部对象,该对象的虚表不会发生变化]) {oParent.Print();} void Test2(CParent oParent[这里会在栈空间内重新构造一个CParent类的对象,如果传入实参的类型与CParent不同则虚表会发生变化]) {oParent.Print();} main() { CSon * p = new CSon(); Test1(*p); //这里只是一个引用 Test2(*p); //这里会在栈空间重新构造Cparent类对象 delete p; } 7.请问下面这段程序的输出结果是_D_: A. 2,1, B. 2,2, C. 1,1, D. 1,2, class CParent { public: CParent() {} virtual ~CParent() {} public: void Print(){ std::cout << "1," ; }; }; class CSon : public CParent { public: CSon() {} virtual ~CSon() {} public: void Print(){ std::cout << "2,"; }; }; main() { CSon oSon; CParent * pParent = &oSon; CSon * pSon = &oSon; pParent->Print(); pSon->Print();[由于父类和子类的Print函数都非虚,所以根据指针类型决定调用关系] } 8.请问下面这段程序的输出结果是_C_: A. 2,1, B. 2,2, C. 1,2, D. 1,1, class CParent { public: CParent() {Print();} virtual ~CParent() {} public: virtual void Print(){ std::cout << "1,"; } }; class CSon : public CParent { public: CSon() {Print();} virtual ~CSon() {} public: void Print(){ std::cout << "2,"; } }; main() { CParent * pParent = new CSon()[类的构造过程遵循压栈原则,构造过程中虚表尚未建立成功,是静态调用虚函数]; delete pParent; } 9.请问下面这段程序的输出结果是_D_: A. 2,2, B. 2, C. 输出结果不确定 D. 以上都不对 class CParent { public: CParent() {Print();[构造子类对象时调用到父类的构造函数,但父类的Print函数是纯虚的,没有实现,所以这里的调用不成功,编译会出错]} virtual ~CParent() {} public: virtual void Print() = 0; }; class CSon : public CParent { public: CSon() {Print();} virtual ~CSon() {} public: void Print() { std::cout << "2,"; }; }; main() { CParent * pParent = new CSon(); delete pParent; } 10.请仔细阅读以下程序: class Base { public: virtual bool operator == (int iValue) { std::cout << "I am Base class !" << std::endl; return true; } virtual ~Base(){} }; class Derive: public Base { public: virtual bool operator == (int iValue) { std::cout << "I am Derive class !" << std::endl; return true; } virtual ~Derive(){} }; int main() { Derive derive; Base* pBase = &derive; Derive* pDerive = &derive; *pBase == 0; *pDerive == 0;[重载操作符声明为virtual使操作符产生多态性] return 0; } 程序的输出结果是_B_: A、I am Base class ! I am base class ! B、I am Derive class ! I am Derive class ! C、I am base class ! I am Derive class ! D、I am Derive class ! I am Base class !
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Luckys-Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值