C++继承

继承的概念及定义

概念(继承的本质就是复用)

继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用
class Person
{
public:
 void Print()
 {
 cout << "name:" << _name << endl;
 cout << "age:" << _age << endl;
 }
protected:
 string _name = "peter"; // 姓名
 int _age = 18;  // 年龄
};

class Student : public Person
{
protected:
 int _stuid; // 学号
};

class Teacher : public Person
{
protected:
 int _jobid; // 工号
};

    Person p;
    p.Print();
    Student s1;
    s1.Print();

    Teacher t1;
    t1.Print();

子类继承后父类的 Person 的成员(成员函数 + 成员变量)都会变成子类的一部分。这里体现出了
Student Teacher 复用了 Person 的成员。

 定义

父类是Person类,也叫基类,子类是Student,也叫派生类

继承关系与访问限制符

继承关系和访问限制符所用的关键字都是一样的,分别是public,protected,private

不同的继承方式和不同访问限制符所继承的用法是不一样的:

总的来说,就是基类中的private成员不管用什么方式都不可见,也意味着语法上是限制使用的(子类里面和外面都不能用),而类里面的protected和public成员要看继承的方式,谁小就用谁;

因此基类的private成员咋派生类中不可见,若基类成员不想在类外直接被访问,但需要在

派生类中能访问,就定义为protected。
关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过 最好显示的写出继承方式
在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承
基类和派生类对象赋值转换
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片
或者切割。寓意把派生类中父类那部分切来赋值过去。基类对象不能赋值给派生类对象
基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
的指针是指向派生类对象时才是安全的。
//子类对象可以赋值给父类的对象/指针/引用
    Person p1 = s1;
    Person* pp1 = &s1;
//中间没有生成临时对象,因为临时对象具有常性,要用const修饰,所以是切割
    Person& rp = s1;
//可以把父类的指针强制类型转换为子类的指针
    Person* pp = &s1;
    Student* s = (Student*) pp;

隐藏/重定义

1. 在继承体系中 基类 派生类 都有 独立的作用域
2. 子类和父类中有同名成员, 子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,
也叫重定义。 (在子类成员函数中,可以 使用 基类 :: 基类成员 显示访问
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏,无关乎参数和返回值类型
4. 注意在实际中在 继承体系里 面最好 不要定义同名的成员

子类的默认构造函数

要显示的调用父类

1.子类的构造必须调用父类默认构造来初始化父类的成员,如果没有显示调用,那么编译器会自动调用父类的默认构造,如果没有默认构造,就要在子类的初始化列表中显示的调用父类构造

2.子类构造先调用父类的构造函数来构造父类,后构造子类

3.子类的拷贝构造必须显示的调用父类的拷贝构造,如果没有显示的调用,那么编译器会自动调用父类的默认构造来构造父类的成员,达不到拷贝构造的要求

4.子类的赋值重载必须显示的调用父类的赋值重载。

编译器自动调用父类

5.子类的析构函数会自动调用父类的析构来保证先子后父的顺序,子类可以访问父类成员,而父类不会访问子类成员,因此要先析构子类,后析构父类。

class Person
{
public:
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)
		: _name(p._name)
	{
		
			cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student(const char* name= "zhangsan", int num=0)
		:Person(name)
		 ,_num(num)
	{
		cout << "Student(const char* name= ""zhangsan"", int num=0)" << endl;
	}
	Student(const Student& s)
		:Person(s)
	{
		cout << "Student(const Student& s)" << endl;
		_num = s._num;
	}
	Student& operator=(const Student& s)
	{
		cout << "Student& operator=(const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator=(s);
//必须要指明是父类,因为构成隐藏,那么子类会优先调用子类的赋值函数,造成栈溢出
			_num = s._num;
		}
		return *this;
	}
	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};

继承与友元

子类不能继承父类的友元,除非父类的友元称为子类的友元。

class Student;
class Person
{
public:
 friend void Display(const Person& p, const Student& s);
protected:
 string _name; // 姓名
};
class Student : public Person
{
 friend void Display(const Person& p, const Student& s);
protected:
 int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
 cout << p._name << endl;
 cout << s._stuNum << endl;
}
void main()
{
 Person p;
 Student s;
 Display(p, s);
}

继承与静态成员

静态成员同时属于父类和子类,在派生类中不会在拷贝一份,只继承使用权

基类定义了 static 静态成员,则整个继承体系里面只有一个这样的成员
class Person
{
public :
 Person () {++ _count ;}
protected :
 string _name ; // 姓名
public :
 static int _count; // 统计人的个数。
};
int Person :: _count = 0;
class Student : public Person
{
protected :
 int _stuNum ; // 学号
};

如果要统计个数,可以直接在父类的构造中计数,因为构造子类要先调用父类的构造函数来构造父类成员。

菱形继承

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。

一个类可以继承多个类,因此就会造成菱形继承。

菱形继承:菱形继承是多继承的一种特殊情况。

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。
Assistant 的对象中 Person 成员会有两份。
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a ;
a . _name = "peter" ;
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a . Student::_name = "xxx" ;
a . Teacher::_name = "yyy" ;

如何解决呢?虚继承

class Person
{
public :
 string _name ; // 姓名
};
class Student : virtual public Person
{
protected :
 int _num ; //学号
};
class Teacher : virtual public Person
{
protected :
 int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected :
 string _majorCourse ; // 主修课程
};
void Test ()
{
 Assistant a ;
 a._name = "peter";
}

在实际中最后不要使用多继承。

  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值