当前位置: 首页 > wzjs >正文

在线教育做网站好还是app好网络营销公司业务范围

在线教育做网站好还是app好,网络营销公司业务范围,京东商城商务网站建设目的,如何查询网站关键词密度目录 一、继承与友元 二、继承与静态成员 三、菱形继承及菱形虚拟继承 1. 继承的方式 2. 菱形继承的问题 3. 菱形虚拟继承 4. 虚拟继承解决数据冗余和二义性的原理 4.1 普通菱形继承的内存布局 4.2 虚拟继承的内存布局 四、继承的总结和反思 1. 多继承的复杂性 2.…

目录

一、继承与友元

二、继承与静态成员

三、菱形继承及菱形虚拟继承

1. 继承的方式

2. 菱形继承的问题 

3. 菱形虚拟继承 

4. 虚拟继承解决数据冗余和二义性的原理

4.1 普通菱形继承的内存布局

4.2 虚拟继承的内存布局

四、继承的总结和反思

1. 多继承的复杂性

2. 继承与组合

2.1 继承(is-a关系)

2.2 组合(has-a关系)

3. 优先使用组合

4. 小结

五、笔试面试题解析

1. 什么是菱形继承?菱形继承的问题是什么?

2. 什么是菱形虚拟继承?如何解决数据冗余和二义性?

3. 继承和组合的区别?什么时候用继承?什么时候用组合?


一、继承与友元

        友元关系不能被继承。也就是说,基类中声明的友元函数或友元类不能访问派生类的私有或保护成员。因为友元关系是类之间的一种特殊访问权限约定,它不具备继承性,派生类并不会自动继承基类的友元所拥有的特殊访问权。

        在下面代码中,Display 函数是 Person 类的友元,因此它可以访问 Person 类的保护成员 _name。然而,Display 函数并不是 Student 类的友元,因此它不能访问 Student 类的保护成员 _stuNum。这表明友元关系不具备继承性,派生类不会自动继承基类的友元声明。

#include <iostream>
#include <string>
using namespace std;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; // 可以访问,因为Display是Person的友元cout << s._stuNum << endl; // 编译错误,Display不是Student的友元
}void main()
{Person p;Student s;Display(p, s);
}

        若想让 Display 函数能够访问派生类 Student 的私有或保护成员,需要在 Student 类中显式地声明 Display 函数为友元: 

class Student : public Person
{
public:friend void Display(const Person& p, const Student& s); // 声明Display是Student的友元
protected:int _stuNum;
};

二、继承与静态成员

        基类中定义的静态成员在整个继承体系中是共享的,无论派生出多少个子类,都只有一个该静态成员的实例。静态成员属于类而非某个具体对象,所以在继承体系中,所有类和对象都共用这一个静态成员,对它的访问和修改会影响整个继承体系中的所有相关部分。

        在基类Person中定义了一个静态成员变量_count,用于统计实例化的对象数量:

#include <iostream>
#include <string>
using namespace std;class Person
{
public:Person() { ++_count; } // 构造函数中自增_count
protected:string _name; // 姓名
public:static int _count; // 静态成员变量,统计人的个数
};int Person::_count = 0; // 静态成员变量在类外初始化class Student : public Person
{
protected:int _stuNum; // 学号
};class Graduate : public Student
{
protected:string _seminarCourse; // 研究科目
};void TestPerson()
{Student s1;Student s2;Student s3;Graduate s4;cout << "人数 : " << Person::_count << endl; // 输出创建的对象数量Student::_count = 0; // 通过Student类修改_count的值cout << "人数 : " << Person::_count << endl; // 输出修改后的值
}int main()
{TestPerson();return 0;
}

        在上述代码中,无论创建多少个StudentGraduate对象,Person::_count始终是一个实例。这是因为静态成员变量属于类本身,而不是类的某个特定对象。我们可以通过打印Person类和Student类中静态成员_count的地址来证明它们是同一个变量: 

cout << &Person::_count << endl; // 输出Person类中_count的地址
cout << &Student::_count << endl; // 输出Student类中_count的地址

三、菱形继承及菱形虚拟继承

1. 继承的方式

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

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

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


2. 菱形继承的问题 

        在多继承情况下,若一个类有两个或以上基类,而这些基类又有一个共同的基类,就可能形成菱形继承。这种情况下容易出现数据冗余(同一个基类被多次继承,导致其成员在派生类对象中重复出现)和二义性(访问共同基类的成员时,编译器无法确定具体访问哪一个基类路径中的成员)问题。

        从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。 在Assistant的对象中Person成员会有两份。

class Person
{
public:string _name; // 姓名
};class Student : public Person
{
protected:int _num; // 学号
};class Teacher : public Person
{
protected:int _id; // 职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};void Test()
{Assistant a;// 这里会报错:二义性成员访问,因为 Assistant 同时从 Student 和 Teacher 继承了 _namea._name = "peter";
}

        通过显式指定父类可以解除二义性,但会导致数据冗余(每个父类都有自己的 _name 成员)。

a.Student::_name = "xxx";
a.Teacher::_name = "yyy"; 

3. 菱形虚拟继承 

        为了解决菱形继承的二义性和数据冗余问题,出现了虚拟继承。通过将继承方式改为虚拟继承,可以让派生类共享同一个基类的实例,从而解决数据冗余和二义性问题。在虚拟继承中,编译器会采用特殊的机制,如虚基表虚基表指针,来确保基类在内存中只出现一次,并且正确地解析对基类成员的访问路径。

#include <iostream>
#include <string>
using namespace std;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; //主修课程
};int main()
{Assistant a;a._name = "peter"; //不再二义性cout << a.Student::_name << endl; // 输出 "peter"cout << a.Teacher::_name << endl; // 输出 "peter"cout << &a.Student::_name << endl; // 地址相同cout << &a.Teacher::_name << endl; // 地址相同return 0;
}

4. 虚拟继承解决数据冗余和二义性的原理

        为了研究虚拟继承原理,我们写一个简化的菱形继承体系,再借助内存窗口观察对象成员的模型。 

4.1 普通菱形继承的内存布局

#include <iostream>
using namespace std;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;
}

在普通菱形继承中,类D对象的内存布局如下:

  • 首先是类B继承自类A的部分,包含 _a(来自A)和 _b

  • 然后是类C继承自类A的部分,再次包含 _a(来自A)和 _c

  • 最后是类D自己的成员 _d

这种布局导致了两个_a成员的存在。

4.2 虚拟继承的内存布局

#include <iostream>
using namespace std;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各自包含一个虚基表指针(vbptr),这些指针指向虚基表(vbtbl)。

  • 虚基表中存储了从类B和类C到共享类A实例的偏移量。

  • D对象中只包含一份类A的成员 _a,并且这个_a被放置在内存布局的最后。

        这里可以分析出D对象中将A放到了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。


下面是上面的Person关系菱形虚拟继承的原理解释: 

四、继承的总结和反思

1. 多继承的复杂性

        很多人觉得C++语法复杂,多继承就是一个典型例子。多继承引入后,很容易形成菱形继承结构,而菱形继承又需要通过虚拟继承来解决数据冗余和二义性问题。这些机制在底层实现上非常复杂。因此,一般不建议设计多继承结构,尤其是菱形继承。它不仅会增加代码的复杂度,还可能对性能产生负面影响。

2. 继承与组合

        在面向对象编程中,继承组合是两种重要的复用机制。它们各有特点,适用于不同的场景。

2.1 继承(is-a关系)

        继承体现的是“is-a”的关系。例如,BMW继承自Car,因为BMW是一种汽车。通过继承,BMW类可以复用Car类的功能和属性。然而,继承也被称为“白箱复用”,因为基类的内部实现对子类是可见的。这种高可见性意味着基类的任何修改都可能影响到所有子类,导致子类和基类之间的耦合度很高。

class Car 
{
protected:string _colour = "白色"; // 颜色string _num = "甘EBIT00"; // 车牌号
};class BMW : public Car 
{
public:void Drive() {cout << "好开-操控" << endl;}
};

2.2 组合(has-a关系)

        组合体现的是“has-a”的关系。例如,Car类组合了Tire类,因为汽车有一个或多个轮胎。组合被称为“黑箱复用”,因为被组合对象的内部实现对组合类是不可见的。这种方式耦合度低,组合类之间的依赖关系较弱。

class Tire 
{
protected:string _brand = "Michelin";  // 品牌size_t _size = 17;         // 尺寸
};class Car 
{
protected:string _colour = "白色"; // 颜色string _num = "甘EBIT00"; // 车牌号Tire _t; // 组合的轮胎对象
};

3. 优先使用组合

        尽管继承在某些场景下非常有用,但在实际开发中,我们应优先使用组合。组合的低耦合特性使得代码更易于维护和扩展。继承则更适合用于那些具有明确“is-a”关系的场景,或者当需要实现多态性时。

4. 小结

  • 继承:适合“is-a”关系,复用基类实现,但耦合度高。

  • 组合:适合“has-a”关系,通过组合对象实现功能,耦合度低。

五、笔试面试题解析

1. 什么是菱形继承?菱形继承的问题是什么?

菱形继承:

        菱形继承是一种多继承的特殊情况,其类层次结构形如菱形。具体来说,一个基类派生出两个或多个子类,然后另一个类又同时继承自这两个子类。例如:

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

        在这里,类 D 同时继承自类 B 和类 C,而类 B 和类 C 又都继承自类 A,从而形成了菱形的继承结构。

主要问题:

  1. 数据冗余:由于多继承路径,基类 A 的成员在派生类 D 中会存在多个副本(每个继承路径各一份)。这不仅浪费内存空间,还可能导致数据不一致的问题。

  2. 二义性:在访问基类 A 的成员时,编译器无法确定应该通过哪条继承路径(BC)来访问该成员,从而引发编译错误。例如

    D d;
    d.a_member = 1; // 错误:无法确定是通过 B 还是 C 访问 a_member

2. 什么是菱形虚拟继承?如何解决数据冗余和二义性?

菱形虚拟继承:

        菱形虚拟继承是一种通过使用 virtual 关键字来解决菱形继承问题的机制。具体来说,在继承基类时使用 virtual 关键字,可以让所有派生类共享同一个基类实例。例如:

class A {};
class B : virtual public A {}; // 虚拟继承
class C : virtual public A {}; // 虚拟继承
class D : public B, public C {};

解决数据冗余和二义性的原理

  1. 共享基类实例:虚拟继承确保在派生类 D 中,基类 A 的成员只存在一个副本。这样就消除了数据冗余。

  2. 消除二义性:通过共享同一个基类实例,访问基类成员时不再需要指定继承路径,编译器可以明确地找到唯一的成员实例。例如

    D d;
    d.a_member = 1; // 正确:直接访问共享的 a_member

内存布局的变化

        使用虚拟继承后,派生类 BC 中会包含虚基表指针(vbptr),这些指针指向虚基表(vbtbl),而虚基表中存储了从派生类到共享基类的偏移量。这样,派生类 D 对象中只包含一份基类 A 的成员,并且可以通过虚基表指针找到该成员。

3. 继承和组合的区别?什么时候用继承?什么时候用组合?

继承(is-a关系)

  • 特点:派生类是基类的特化(如“猫是动物”),可复用基类接口并扩展功能。

  • 适用场景

    • 需要实现多态(如虚函数)。

    • 明确逻辑上的层次关系(如GUI控件继承自基类Widget)。

  • 缺点:高耦合性,基类修改可能影响所有派生类。

组合(has-a关系)

  • 特点:类通过包含其他类的对象实现功能复用(如“汽车包含引擎”)。

  • 适用场景

    • 需要复用功能但无需继承接口(如Stack类组合vector实现存储)。

    • 降低耦合,提升代码灵活性(组合类可替换成员对象实现不同功能)。

  • 优点:封装性好,维护成本低。

选择原则

  • 优先组合:除非需要多态或明确的层次关系,否则优先使用组合。

  • 谨慎继承:避免复杂的继承链,尤其是菱形继承,尽量使用虚继承解决冗余问题。

http://www.dtcms.com/wzjs/178174.html

相关文章:

  • 美食网站开发开题报告chrome谷歌浏览器
  • 免费html5中文网站素材怎么可以在百度发布信息
  • 咸阳北京网站建设怎样创建网站
  • 做毕设网站多少钱360关键词排名百度
  • 手机端网站怎么做个人网站该怎么打广告
  • 滁州网站建设在百度上怎么发布信息
  • 查公司法人信息怎么查百度网站怎样优化排名
  • 克拉玛依建设局网站6微博指数查询入口
  • 门户网站建设公司流程惠州seo整站优化
  • 网站平台做推广seo网站推广的主要目的
  • html5网站强制横屏站长工具综合查询ip
  • 网站开发前端要学什么软件免费关键词排名优化软件
  • 贸易做网站seo搜索引擎优化营销案例
  • 怎么描述网站主页做的好今日头条新闻大事件
  • 建筑人才网站哪个比较好百度的合作网站有哪些
  • 多语言网站制作竞价推广公司
  • 网站空间购买哪个好外贸怎么建立自己的网站
  • 怎么为一个网站做外链站长工具四叶草
  • 百度收录网站的图片潜江seo
  • 能看任何网站的浏览器厦门seo培训
  • 那些网站可以上传自己做的视频百度代理授权查询
  • 好的外贸网站特点百度号码认证
  • 宛城区微网站开发网站友情链接交易平台
  • 郑州怎么做网站排名google play下载官方版
  • 做关于时尚网站的目的互联网广告销售
  • 怎样新建网站网络营销策略实施的步骤
  • 做网站运营的简历关键词的选取原则
  • 网站设计 开发人员一个完整的产品运营方案
  • 微信网站怎么制作seo网站优化师
  • 南宁做网站的公司有哪些深圳市seo上词贵不贵