您的位置:首页 > 游戏 > 手游 > 珠海市研发网站建设_进入微信公众号首页_福建键seo排名_百度经验官网首页

珠海市研发网站建设_进入微信公众号首页_福建键seo排名_百度经验官网首页

2024/11/15 18:38:00 来源:https://blog.csdn.net/m0_74626010/article/details/143191450  浏览:    关键词:珠海市研发网站建设_进入微信公众号首页_福建键seo排名_百度经验官网首页
珠海市研发网站建设_进入微信公众号首页_福建键seo排名_百度经验官网首页

一、继承的概念

        继承是指,在原有类的基础上扩展出新的功能,产生新的类,叫做派生类

二、继承的格式

       

        继承方式和访问限定符不同时,继承基类成员的变化      

         总结:

        1、当以 public 方式继承时,基类中的 public 成员还是派生类的 public 成员,protected 成员还是 protected 成员,private  在派生类中不可见。(以 public 方式继承时,与其在基类中限定符一样,除 private 是不可见)

        2、当以 protected 方式继承时,基类的 public 和 protected 成员在派生类中都是 protected 成员,基类中的 private 成员依旧是不可见。(基类中比 protected 等级低的在派生类中都是 protected成员,除 private 不可见)

        3、当以 private方式继承时,基类的 public 和 protected 成员在派生类中都是 private成员,基类中的 private 成员依旧是不可见。(基类中比 private等级低的在派生类中都是 private成员,除 private 不可见)

三、基类和派生类的对象赋值兼容转换

        派生类可以赋值给基类的 对象/指针/引用。这种方式也叫做切割或切片。

class Person
{
protected:string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
private:string _id;// 学号
};

      

        在进行赋值时,父类只拿到子类中属于父类的那部分,而丢弃了只属于子类中的部分,就像切割了一样,因此这种赋值也叫做切割或切片。(这个赋值是天然的,并不会产生临时变量

Student s("la", "女", 17, "10201");
Person& rp = s;

         在赋值给父类的引用时,rp 改变了成员变量,s 中的成员变量也会跟着改变(因为赋值只是把子类 s 中父类的地址给 rp 即可)

四、隐藏

        如果基类和派生类中有同名的成员,就构成隐藏,子类将屏蔽父类的同名成员。

class Person
{
public:Person(string name = "lw", string sex = "男", int age = 18):_name(name),_sex(sex),_age(age){}string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public:Student(string name, string sex, int age, string id):Person(name, sex, age),_id(id),_age(age){}string _id;// 学号int _age;
};int main()
{Student s("la", "女", 17, "10201");s._age = 100;return 0;
}

        上述代码有同名成员变量,将 _age 改为 100,默认改的是子类中的 _age 。

        可以指定使用 父类 的作用域,来访问 父类中的 _age。

Student s("la", "女", 17, "10201");
s.Person::_age = 100;

        同样,成员函数也可以构成隐藏。

class Person
{
public:Person(string name = "lw", string sex = "男", int age = 18):_name(name),_sex(sex),_age(age){}void func(){cout << "Person::func()" << endl;}
public:string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public:Student(string name, string sex, int age, string id):Person(name, sex, age),_id(id),_age(age){}void func(int x){cout << "Student::func(int x)" << endl;}string _id;// 学号int _age;
};

  上述代码中的 func 函数就是构成隐藏,只要是同名成员函数就构成隐藏。与返回值和参数无关

 五、派生类的默认成员函数

    1、构造函数

        派生类的成员分为:

        ①、派生类的成员:自定义类型和内置类型分别处理

        ②、父类的成员:调用父类的构造函数

class Person
{
public:Person(string name = "lw", string sex = "男", int age = 18):_name(name),_sex(sex),_age(age){std::cout << "Person::Person()" << endl;}string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public:Student(string id = "2024593"):_id(id){std::cout << "Student::Student()" << endl;}string _id;// 学号
};int main()
{Student s;return 0;
}

        运行结果: 

             

         从上述代码可以看出,子类会先调用父类的构造函数,再调用自己的构造函数。

      如果父类没有默认构造函数可以使用 Person(...) 在初始化列表显示调用。(如 四 隐藏中的代码)

    2、拷贝构造: 

class Person
{
public:Person(string name = "lw", string sex = "男", int age = 18):_name(name),_sex(sex),_age(age){cout << "Person::Person()" << endl;}Person(const Person& p):_name(p._name),_sex(p._sex),_age(p._age){cout << "Person::Person(const Person& p)" << endl;}string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public:Student(string id = "2024593"):_id(id){cout << "Student::Student()" << endl;}Student(const Student& s):Person(s), // 父类调用父类的拷贝构造_id(s._id){cout << "Student::Student(const Student& s)" << endl;}string _id;// 学号
};int main()
{Student s;Student s_copy = s;return 0;
}

        3、赋值重载

#include <iostream>using namespace std;class Person
{
public:Person(string name = "lw", string sex = "男", int age = 18):_name(name),_sex(sex),_age(age){cout << "Person::Person()" << endl;}Person(const Person& p):_name(p._name),_sex(p._sex),_age(p._age){cout << "Person::Person(const Person& p)" << endl;}Person& operator=(const Person& p){if(&p != this) // 如果不是自己给自己赋值{_name = p._name;_sex = p._sex;_age = p._age;cout << "operator=(const Person& p)" << endl;}return *this;}string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public:Student(string id = "2024593"):_id(id){cout << "Student::Student()" << endl;}Student(const Student& s):Person(s), // 父类调用父类的拷贝构造_id(s._id){cout << "Student::Student(const Student& s)" << endl;}Student& operator=(const Student& s){if(&s != this) // 如果不是自己给自己赋值{Person::operator=(s); // 显示调用父类的赋值重载_id = s._id;cout << "operator=(const Student& s)" << endl;}return *this;}string _id;// 学号
};int main()
{Student s;s._age = 100;Student s_copy;s_copy = s;return 0;
}

        4、析构函数

        子类析构时会自动调用父类的析构函数,不需要显示自己写。

        原因是为了保证析构顺序:先子后父。

        构造:先父后子;子类构造可能会使用父类的成员

        析构:先子后父;子类析构可能需要访问父类的成员

class Person
{
public:Person(string name = "lw", string sex = "男", int age = 18):_name(name),_sex(sex),_age(age){cout << "Person::Person()" << endl;}Person(const Person& p):_name(p._name),_sex(p._sex),_age(p._age){cout << "Person::Person(const Person& p)" << endl;}Person& operator=(const Person& p){if(&p != this) // 如果不是自己给自己赋值{_name = p._name;_sex = p._sex;_age = p._age;cout << "operator=(const Person& p)" << endl;}return *this;}~Person(){cout << "~Person()" << endl;}string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public:Student(string id = "2024593"):_id(id){cout << "Student::Student()" << endl;}Student(const Student& s):Person(s), // 父类调用父类的拷贝构造_id(s._id){cout << "Student::Student(const Student& s)" << endl;}Student& operator=(const Student& s){if(&s != this) // 如果不是自己给自己赋值{Person::operator=(s);_id = s._id;cout << "operator=(const Student& s)" << endl;}return *this;}~Student(){cout << "~Student()" << endl;}string _id;// 学号
};int main()
{Student s;return 0;
}

        子类的析构函数和父类的析构函数构成重载,因为由于多态原因,析构函数被特殊处理,函数名都会被处理成 destruction()。

        总结:派生类这些默认成员函数的规则与以前类似,不同的是分为了子类部分和父类部分,父类部分就调用父类的默认成员函数。

六、友元与静态成员函数

        1、父类的友元无法继承

        2、父类的静态成员在整个继承体系中只有一个

                (例:static int count; // 则父类和子类都有count,它们是同一个)

class Person
{
public:string _name; // 姓名string _sex;  // 性别int _age; // 年龄static int count;
};int Person::count = 1;class Student : public Person
{
public:string _id;// 学号
};int main()
{Person p;Student s;cout << &p.count << endl;cout << &s.count << endl;return 0;
}

        同一个地址,说明是同一个 count。

六、菱形继承

        单继承:一个子类只有一个直接父类 


         多继承:一个子类有多个直接父类


菱形继承:多继承的两个父类继承了同一个类 

        菱形继承出现的问题:在 Assistant 类中,Person 类会有两份。

        会出现数据冗余与二义性的问题。

class Person
{
public:string _name; // 姓名
};class Student : public Person
{
public:string _sid;// 学号
};class Teacher : public Person
{
public:string _tid;// 教工号
};class Assistant : public Teacher, public Student
{
public:string _major;// 专业
};int main()
{Assistant a;a._name = "la";return 0;
}

  

        虽然我们可以用指定类域的方式解决二义性问题(如:Student::_name),但有很多情况我们不需要两份 Person,会造成空间的浪费。

        我们可以增加虚继承来解决数据冗余和二义性的问题。

        虚继承的方式:就是在有二义性的地方( Student 和 Teacher )继承方式前加 virtual

class Person
{
public:string _name; // 姓名
};class Student : virtual public Person
{
public:string _sid;// 学号
};class Teacher : virtual public Person
{
public:string _tid;// 教工号
};class Assistant : public Teacher, public Student
{
public:string _major;// 专业
};int main()
{Assistant a;a._name = "la";return 0;
}

         监视窗口:

        虚拟继承原理:原本 Student 中和 Teacher 中各有一个类 Person,但使用虚拟继承后,Student 和 Teacher 有一份公共的类 Person。Student 中和 Teacher 中不保存类 Person ,而是保存 Person 类的偏移量的地址。

        公共的类 Person 一般是在最后面(但没规定,也可以不在)

         

菱形继承的对象模型

       在VS环境下

class A
{
public:int _a;
};class B :public A
{
public:int _b;
};class C :public A
{
public:int _c;
};class D :public B, public C
{
public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

         没有使用虚拟继承时的 监视内存窗口:


 虚拟继承后 (环境为VS,X86): 

class A
{
public:int _a;
};class B : virtual public A
{
public:int _b;
};class C : virtual public A
{
public:int _c;
};class D :public B, public C
{
public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

         内存窗口:         

        等于在虚拟继承中, D中的 B 和 C 都存的是 A 偏移量的地址,形成了偏移量表,所有的 D 对象都有一份偏移量表,用来找公共的 A。

        再定义一个 D dd;

        它们有相同的偏移量表。

        同理,在 B 和C 对象中也是存了偏移量表和公共的 A。

        它们的模型是一样的。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com