c++的三大特性之关于继承

目录

继承的概念及定义

基类和派生类对象赋值转换

继承中的作用域

派生类的默认成员函数

继承与友元,静态成员


继承的概念及定义

概念:

继承 (inheritance)机制是面向对象程序设计使代码可以复用 的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展 ,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构 ,体现了由简单到复杂的认知过程。继承是类设计层次的复用

示例:

class Person
{
public:
 void Print()
 {
     cout << "name:" << _name << endl;
     cout << "age:" << _age << endl;
 }

protected:
     string _name = "Andy"; // 姓名
     int _age = 18;  // 年龄
};

// 继承后父类Person的成员(成员函数+成员变量)都会变成子类的一部分。
class Student : public Person
{
protected:
     int _stuid; // 学号
};

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

**定义:**Person是父类,也称作基类。Student是子类,也称作派生类。

继承关系和访问限定符


继承基类成员访问方式的变化

总结:

    1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它
    1. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
    1. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected> private。
    1. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过****最好显示的写出继承方式
    1. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强
      建议使用:

基类和派生类对象赋值转换

1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫 切片或者切割 。寓意把派生类中父类那部分切来赋值过去。
2.基类对象不能赋值给派生类对象。
3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。

class Person
{
protected :
     string _name; // 姓名
     string _sex;  // 性别
     int _age; // 年龄
};

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

void Test ()
{
     Student sobj ;
 // 1.子类对象可以赋值给父类对象/指针/引用
     Person pobj = sobj ;
     Person* pp = &sobj;
     Person& rp = sobj;
    
 //2.基类对象不能赋值给派生类对象
    sobj = pobj;
    
    // 3.基类的指针可以通过强制类型转换赋值给派生类的指针
    pp = &sobj
    Student* ps1 = (Student*)pp; // 这种情况转换时可以的。
    ps1->_No = 10;
}
注意:在公有继承下,子类赋值给基类是天然支持的,不存在隐式类型转换

继承中的作用域

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

派生类的默认成员函数(很重要,画圈圈)

**"默认"**的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个成员函数是如何生成的呢?

    1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
    1. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
    1. 派生类的operator=必须要调用基类的operator=完成基类的复制。
    1. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
    1. 派生类对象初始化先调用基类构造再调派生类构造。
    1. 派生类对象析构清理先调用派生类析构再调基类的析构
    1. 因为一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

    class Person
    {
    public:
    Person(const char* name = "Andy")
    : _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, int num)
    //:_name(name) error
    :Person(name) //需要显示调用
    ,_num(num)
    {
    cout << "Student()" << endl;
    }

      //这里调用父类的拷贝构造需要传过去一个父类对象
      //那么如何拿到一个父类的对象呢? ---  切片
      Student(const Student& s)
      	:Person(s)
      	,_num(s._num)
      {
      	cout << "Student(const Student& s)" << endl;
      }
    
      Student operator=(const Student& s)
      {
    
      	if (this != &s)
      	{
      		//operator=(s); //这里构成隐藏
      		Person::operator=(s);
      		_num = s._num;
      	}
    
      	cout << "Student operator=(const Student& s)" << endl;
    
    
      	return *this;
      }
    
      //由于多态的原因,析构函数会被处理成destructor
      //destructor(){}
      ~Student()  //
      {
      	//Person::~Person();  //这里构成隐藏
    
      	cout << "~Student()" << endl;
      }
      //注:子类的析构函数完成时,会自动调用父类析构,保障先析构子,在析构父
    

    protected:
    int _num; //学号
    };

    int main()
    {
    Student s("Andy",18);
    //Student s1(s);

      //Person p = s;
    
      //s = s1;
    
      return 0;
    

    }

继承与友元,静态成员

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

class Student;
class Person
{
public:
     friend void Display(const Person& p, const Student& s);
protected:
     string _name; // 姓名
};

class Student : public Person
{
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静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

class Person
{
public:
	Person() { ++_count; }
public:
	string _name; // 姓名

public:
	static int _count; // 统计人的个数。
};
int Person::_count = 0;

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

class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究科目
};


int main()
{
	Person p1;
	Student s1;
	cout << &p1._name << endl;
	cout << &s1._name << endl;

	cout << &p1._count << endl;
	cout << &s1._count << endl;

	Graduate g1;
	Graduate g2;

	cout << Person::_count << endl;
	cout << Graduate::_count << endl;
	//父类静态成员子类共享


	return 0;
}

以上仅供参考,欢迎讨论

相关推荐
疯狂吧小飞牛32 分钟前
无锁编程–C语言
c语言·数据结构·c++·算法
cccccc语言我来了42 分钟前
详解 【AVL树】
数据结构·c++
wqyc++1 小时前
C++ 中的 Lambda 表达式
开发语言·c++
灰末1 小时前
[第15次CCFCSP]数据中心
数据结构·c++·算法·图论·kruskal
Qhumaing1 小时前
C/C++学习-引用
c语言·c++·学习
_小柏_2 小时前
C/C++基础知识复习(28)
c语言·c++
一只鸡某2 小时前
实习冲刺第三十一天
数据结构·c++·算法·leetcode·排序算法
牙牙要健康2 小时前
【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程
c++·人工智能·深度学习
AICodeThunder3 小时前
C++知识点总结(58):序列型动态规划
开发语言·c++·动态规划
无限大.3 小时前
C++ String
java·开发语言·c++