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

网站建设与运营策划书南宁优化网站网络服务

网站建设与运营策划书,南宁优化网站网络服务,wordpress页面创建,广东深广东深圳网站建设目录 继承的概念 继承的定义 继承的基本语法 继承类型与访问控制 基类和派生类对象赋值转换 继承中的作用域 派生类的默认成员函数 默认成员函数的关键注意事项 继承与友元 友元关系不可继承 派生类需独立声明友元 友元类与继承 继承与静态成员 静态成员变量的继承…

目录

继承的概念

继承的定义

继承的基本语法

 继承类型与访问控制

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

继承中的作用域

派生类的默认成员函数

 默认成员函数的关键注意事项

继承与友元

友元关系不可继承

派生类需独立声明友元

友元类与继承

继承与静态成员

静态成员变量的继承

派生类定义同名静态变量

总结

继承的方式

虚继承(Virtual Inheritance)

虚继承原理

 继承和组合


继承的概念

  1. 代码复用:子类继承父类的属性和方法,避免重复编写相同代码,提升开发效率。

  2. 层次关系:体现“是一个(is-a)”关系,如“狗是动物”,子类是父类的特化。

// 父类
class Person
{
public:void Print (){cout<<_name <<endl;}    
private :string _name = "Tom" ; // 姓名int _age ; // 年龄
};
// 子类
class Student : public Person
{
protected :int _stunum ; // 学号
};
// 子类
class Teacher : public Person
{
protected:int _jobid;   //工号
};

继承的定义

继承的基本语法

class BaseClass {// 基类成员(数据和方法)
};class DerivedClass : access-specifier BaseClass {// 派生类成员(新增或覆盖基类成员)
};

access-specifier 可以是 publicprotected 或 private,控制基类成员在派生类中的访问权限。

 继承类型与访问控制

C++ 的继承类型通过 访问修饰符 决定基类成员在派生类中的可见性:

基类成员访问权限public 继承protected 继承private 继承
publicpublic in Derivedprotected in Derivedprivate in Derived
protectedprotectedprotectedprivate
private不可访问不可访问不可访问
  • 默认继承方式:若未指定访问修饰符,class 的默认继承是 privatestruct 是 public

  • 关键规则

    • private 成员始终不可被派生类直接访问(需通过基类的 public/protected 方法间接访问)。

    • protected 成员在派生类中可见,但外部不可访问

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

派生类对象赋值给基类对象(向上转型)

  • 向上转型:将派生类对象赋值给基类对象(或通过基类指针/引用操作派生类对象)。
  • 对象切片:当派生类对象直接赋值给基类对象时,仅复制基类部分的成员,派生类特有的成员会被丢弃。

  • 多态失效:若直接赋值基类对象(非指针/引用),虚函数机制失效,无法实现动态绑定。

一、直接赋值(对象切片+多态失效)

class Base 
{ 
public: int _a; virtual void print() { cout << "Base: " << _a << endl; } 
};class Derived : public Base 
{ 
public: int _b; void print() override { cout << "Derived: " << _a << ", " << _b << endl; } 
};Derived d;
d._a = 1;
d._b = 2;Base b = d;  // 对象切片:b 仅保留 Base 部分
b.print();   // 输出 "Base: 1"(调用 Base::print)
  1. 成员丢失b 对象中仅保留 Base 类的成员(如 _a),Derived 类的  _b 被丢弃。

  2. 虚函数失效b.print() 调用的是 Base::print(),而非 Derived::print()

  3. 对象切片b 是一个独立的基类对象,与原 Derived 对象无关联。

二、使用指针/引用

  注意:为避免对象切片,应优先使用 基类指针或引用 操作派生类对象,保留多态特性。

Derived d;Base* pb = &d;       // 正确
pb->print();         // 调用 Derived::print(若 print 是虚函数)Base& pb1 = d;
pb1.print();         // 调用 Derived::print(若 print 是虚函数)
  1. 多态保留:通过虚函数表(vtable)实现动态绑定,调用实际对象的方法。

  2. 无对象切片:指针/引用直接操作原派生类对象,不复制数据。

 对比“对象切片”与“指针/引用”

场景对象切片(直接赋值)指针/引用
派生类对象完整性❌ 被截断(仅保留基类部分)✅ 完整保留
派生类成员访问❌ 物理丢失✅ 存在但需转型访问
多态性❌ 失效(静态绑定)✅ 保留(动态绑定)
典型操作Base b = derived;Base* pb = &derived;
  • 派生类特有成员未被抛弃:通过基类指针/引用操作时,派生类对象完整保留在内存中。

  • 访问限制是类型系统的规则:基类接口无法直接访问派生类特有成员,需通过安全的向下转型。

  • 多态性不受影响:虚函数机制依然有效,确保派生类行为正确执行。

 总结:

操作方式对象切片多态保留适用场景
直接赋值基类对象✅ 发生❌ 失效不推荐
基类指针/引用操作❌ 无✅ 保留推荐(动态绑定)
智能指针管理❌ 无✅ 保留推荐(资源自动释放)

注意: 始终通过指针或引用操作派生类对象,避免直接赋值导致的成员丢失和多态失效。

继承中的作用域

基类与派生类的作用域关系

  • 作用域嵌套

    • 派生类的作用域嵌套在基类的作用域之外,形成独立的命名空间。

    • 基类成员的作用域会延伸到派生类中,但若派生类定义了同名成员,则会 隐藏(Hide) 基类成员。

  • 访问顺序
    当访问一个成员时,编译器优先在 当前类作用域 查找,若未找到则向基类作用域逐级查找。

成员变量的作用域与隐藏

若派生类定义了与基类同名的成员变量,基类的变量会被 隐藏,需显式指定作用域访问。

  • 隐藏而非覆盖:派生类成员变量与基类同名时,基类变量依然存在,但需通过 Base::value 访问。

  • 内存占用:基类和派生类的同名变量会同时存在于派生类对象中。

class Base 
{
public:int value = 10;
};class Derived : public Base 
{
public:int value = 20;  // 隐藏 Base::value
};int main() {Derived d;cout << d.value << endl;        // 输出 20(访问派生类成员)cout << d.Base::value << endl;   // 输出 10(显式访问基类成员)return 0;
}

成员函数的作用域与隐藏

若派生类定义了与基类同名函数(即使参数不同),基类的所有同名函数会被 隐藏

class Base 
{
public:void func() { cout << "Base::func()" << endl; }void func(int x) { cout << "Base::func(int)" << endl; }  // 重载函数
};class Derived : public Base 
{
public:void func() { cout << "Derived::func()" << endl; }  // 隐藏 Base::func() 和 Base::func(int)
};int main() {Derived d;d.func();        // 正确:调用 Derived::func()// d.func(10);   // 错误:Base::func(int) 被隐藏d.Base::func(10); // 正确:显式调用基类函数return 0;
}

解决方法:使用 using 声明

通过 using Base::func; 将基类同名函数引入派生类作用域,避免隐藏:

 

class Derived : public Base 
{
public:using Base::func;  // 引入基类的所有 func 重载void func() { cout << "Derived::func()" << endl; }
};int main() 
{Derived d;d.func();     // 调用 Derived::func()d.func(10);   // 正确:调用 Base::func(int)return 0;
}

总结:

  1. 避免同名成员
    尽量不在派生类中定义与基类同名的非虚成员变量,减少隐藏问题。

  2. 使用 using 声明
    当需要保留基类函数重载时,用 using Base::func; 引入作用域。

  3. 处理多继承冲突
    多继承中同名成员必须显式指定基类作用域。

 

派生类的默认成员函数

默认成员函数,即我们不写编译器会自动生成的函数,类当中的默认成员函数有以下六个:

在派生类中的默认成员函数,如下:

 1、默认构造函数(Default Constructor)

  • 生成条件:派生类未定义任何构造函数时,编译器会生成默认构造函数。

  • 执行逻辑顺序

    1. 调用基类的默认构造函数。

    2. 调用派生类成员的默认构造函数(按声明顺序)。

  • 若基类无默认构造函数

    • 必须显式调用基类的有参构造函数。

class Base {
public:Base(int x) { /* ... */ }  // 无默认构造函数
};class Derived : public Base {
public:// 必须显式调用基类构造函数Derived() : Base(0) { /* ... */ }
};

 2、析构函数(Destructor)

  • 生成条件:派生类未定义析构函数时,编译器生成默认析构函数。

  • 执行逻辑顺序

    1. 执行派生类析构函数体(若用户定义)。

    2. 调用派生类成员的析构函数(按声明逆序)。

    3. 调用基类析构函数。

  • 关键规则

    • 虚析构函数:若基类析构函数为 virtual,派生类析构函数自动成为虚函数。

3、拷贝构造函数(Copy Constructor)

  • 生成条件:派生类未定义拷贝构造函数时,编译器生成默认拷贝构造函数。

  • 执行逻辑顺序

    1. 调用基类的拷贝构造函数。

    2. 拷贝派生类成员(按成员浅拷贝)。

4、拷贝赋值运算符(Copy Assignment Operator)

  • 生成条件:派生类未定义拷贝赋值运算符时,编译器生成默认版本。

  • 执行逻辑

    1. 调用基类的拷贝赋值运算符。

    2. 拷贝派生类成员(按成员浅拷贝)。

5、移动构造函数与移动赋值运算符(C++11+)

  • 生成条件:若派生类未定义移动操作且未显式声明拷贝操作/析构函数,编译器会生成默认移动操作。

  • 执行逻辑

    1. 调用基类的移动构造函数/移动赋值运算符。

    2. 移动派生类成员(按成员移动语义)。

 默认成员函数的关键注意事项

  1. 基类成员的初始化依赖基类构造函数

    • 派生类构造函数必须显式调用基类构造函数(若基类无默认构造函数)。

  2. 深拷贝与资源管理

    • 若派生类或基类包含指针等资源,需手动定义拷贝/移动操作(避免浅拷贝问题)。

  3. 虚析构函数的重要性

    • 若基类指针指向派生类对象,基类析构函数必须为 virtual,否则导致资源泄漏。

  4. 继承链中的成员函数调用顺序

    • 构造:基类 → 派生类成员 → 派生类构造函数体。

    • 析构:派生类析构函数体 → 派生类成员析构 → 基类析构。

继承与友元

友元关系不可继承

基类的友元不会自动成为派生类的友元。派生类必须显式声明自己的友元。

class Base 
{
private:int secret;friend void friendFunction(Base&);  // 声明友元函数
};class Derived : public Base 
{
private:int derivedSecret;
};void friendFunction(Base& b) 
{b.secret = 10;     // 正确:友元可访问 Base 的私有成员
}void friendFunction(Derived& d) 
{d.secret = 20;       // 正确:继承自 Base 的 secret 仍是 Base 的成员,可访问d.derivedSecret = 30; // 错误!友元函数不是 Derived 的友元
}

派生类需独立声明友元

若希望某个函数或类能访问派生类的私有成员,需在派生类中单独声明友元。

class Derived : public Base 
{
private:int derivedSecret;friend void friendFunction(Derived&);  // 显式声明友元
};void friendFunction(Derived& d) 
{d.derivedSecret = 30;  // 正确:Derived 显式声明了友元
}

友元类与继承

  • 友元类的成员函数:友元类的成员函数可以访问声明友元的类的私有成员,但对派生类的访问仍受限制。

class FriendClass;  // 前向声明class Base {
private:int secret;friend class FriendClass;  // FriendClass 是 Base 的友元
};class Derived : public Base {
private:int derivedSecret;
};class FriendClass {
public:void accessBase(Base& b) {b.secret = 10;  // 正确:FriendClass 是 Base 的友元}void accessDerived(Derived& d) {d.secret = 20;        // 正确:secret 是 Base 的成员d.derivedSecret = 30; // 错误!FriendClass 不是 Derived 的友元}
};
特性规则
友元继承性基类的友元不是派生类的友元
派生类友元声明必须显式声明
友元类访问权限可访问基类私有成员,但不可访问派生类新增私有成员(除非显式声明为友元)
设计建议优先使用受保护接口或公有方法,避免过度依赖友元

继承与静态成员

静态成员变量的继承

  • 共享性:基类的静态成员变量会被所有派生类共享,不会为每个派生类创建独立副本

  • 访问方式

    • 通过基类名访问:Base::staticVar

    • 通过派生类名访问:Derived::staticVar(实际仍是基类的静态成员)

  • 初始化静态成员变量必须在类外初始化(通常在基类作用域内)

#include <iostream>
using namespace std;class Base 
{
public:static int count;  // 静态成员声明Base() { count++; }
};
int Base::count = 0;   // 静态成员初始化class Derived : public Base 
{
public:Derived() {}  // 构造函数隐式调用 Base()
};int main() 
{Base b1, b2;Derived d1, d2;cout << "Base::count: " << Base::count << endl;     // 输出 4cout << "Derived::count: " << Derived::count << endl; // 同样输出 4return 0;
}

派生类定义同名静态变量

  • 隐藏基类静态成员:若派生类定义同名静态变量,会隐藏基类的静态成员。

  • 显式访问基类静态成员:需通过作用域运算符 Base::staticVar

 

class Base 
{
public:static int value;  // 基类静态成员
};
int Base::value = 10;class Derived : public Base 
{
public:static int value;  // 隐藏 Base::value
};
int Derived::value = 20;  // 独立于 Base::valueint main() 
{cout << Base::value << endl;    // 输出 10cout << Derived::value << endl; // 输出 20cout << Derived::Base::value << endl; // 输出 10(显式访问基类成员)return 0;
}

总结

特性规则
静态成员变量的继承所有派生类共享基类静态变量,不创建新副本
同名静态成员变量派生类定义同名变量会隐藏基类变量,需显式作用域访问
静态成员函数的继承可调用基类静态函数,但无法覆盖;同名函数隐藏基类版本
多态性静态函数不支持多态,虚函数需通过对象调用
访问权限受基类访问修饰符限制(public/protected/private)

 

继承的方式

继承方式的选择原则

场景推荐方式理由
需要多态和接口继承public 继承保留基类公有接口,支持动态绑定
仅复用实现,不暴露接口private 继承或组合隐藏基类细节,降低耦合(优先选择组合)
需要部分保护基类成员protected 继承基类成员仅在派生类内部使用
多继承需解决菱形问题虚继承(virtual避免基类成员重复

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

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

  • 语法:派生类可以同时继承多个基类。

  • 潜在问题菱形继承(Diamond Problem) 导致成员重复和歧义。

  • 解决方案:使用 虚继承(Virtual Inheritance)

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

菱形继承的问题:从对象成员模型构造,可以看出菱形继承有数据冗余二义性的问题。

  1. 数据冗余
  2. 二义性
#include <iostream>
#include <string>
using namespace std;
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; //主修课程
};
int main()
{Assistant a;a._name = "peter"; //二义性:无法明确知道要访问哪一个_namereturn 0;
}

Assistant对象是多继承的Student和Teacher,而Student和Teacher当中都继承了Person,因此Student和Teacher当中都有_name成员,若是直接访问Assistant对象的_name成员会出现访问不明确的报错。

解决二义性:我们可以显示指定访问Assistant哪个父类的_name成员。

//显示指定访问哪个父类的成员
a.Student::_name = "张同学";
a.Teacher::_name = "张老师";

虽然该方法可以解决二义性的问题,但仍然不能解决数据冗余的问题。因为在Assistant的对象在Person成员始终会存在两份。 

虚继承(Virtual Inheritance)

  • 目的:解决多继承中的菱形继承问题,确保基类在派生类中只有一份实例。

  • 实现机制:通过虚基类指针(vptr)和虚基类表(vtable)管理共享基类。

  • 语法:在继承时添加 virtual 关键字。

#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"; //无二义性return 0;
}

 

虚继承原理

虚继承通过 虚基类指针(Virtual Base Pointer) 和 虚基类表(Virtual Base Table,vbtable) 实现共享基类的间接访问。

内存结构分析

  • 虚基类指针
    每个虚继承的派生类对象内部包含一个指针,指向虚基类子对象的位置。

  • 虚基类表
    编译器生成的一张表,记录虚基类相对于当前对象的偏移量。

我们来做一个简单实验 

一、不使用虚继承 

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类对象当中各个成员在内存当中的分布情况如下:

 D类对象当中各个成员在内存当中的分布情况如下:

二、使用虚继承  

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;
};

D类对象当中的_a成员被放到了最后,而在原来存放两个_a成员的位置变成了两个指针,这两个指针叫虚基表指针,它们分别指向一个虚基表。
虚基表中包含两个数据:

  1. 第一个数据是为多态的虚表预留的存偏移量的位置
  2. 第二个数据就是当前类对象位置距离公共虚基类的偏移量

也就是说,这两个指针经过一系列的计算,最终都可以找到成员_a。

 

 继承和组合

  • 继承是一种is-a的关系,也就是说每个派生类对象都是一个基类对象;
  • 而组合是一种has-a的关系,若是B组合了A,那么每个B对象中都有一个A对象。
特性继承(Inheritance)组合(Composition)
关系类型"is-a"(派生类是基类的一种特化)"has-a"(类包含其他类的对象作为成员)
耦合度高耦合(派生类依赖基类实现)低耦合(通过接口或委托交互)
复用方式直接复用基类代码(白盒复用)通过成员对象的方法调用(黑盒复用)
多态支持天然支持(虚函数)需依赖接口抽象(如纯虚类)
灵活性修改基类会影响所有派生类成员对象可动态替换
设计原则遵循 LSP(里氏替换原则)遵循 SRP(单一职责原则)和 ISP(接口隔离原则)

动物类和狗类、猫类就是is-a的关系,它们之间适合使用继承。 

#include <iostream>
using namespace std;// 基类:动物
class Animal 
{
public:virtual void makeSound() const {cout << "Some animal sound" << endl;}virtual ~Animal() {} // 虚析构函数确保正确释放资源
};// 派生类:狗(继承自动物)
class Dog : public Animal 
{
public:void makeSound() const override {cout << "Woof! Woof!" << endl;}
};// 派生类:猫(继承自动物)
class Cat : public Animal {
public:void makeSound() const override {cout << "Meow! Meow!" << endl;}
};int main() 
{Animal* animals[] = {new Dog(), new Cat()};for (Animal* animal : animals) {animal->makeSound(); // 多态调用// Woof! Woof!// Meow! Meow!delete animal;}return 0;
}

而车和轮胎之间就是has-a的关系,它们之间则适合使用组合。

#include <iostream>
using namespace std;// 引擎类(独立组件)
class Engine 
{
public:void start() const {cout << "Engine starts: Vroom!" << endl;}
};// 车轮类(独立组件)
class Wheel 
{
public:void checkPressure() const {cout << "Wheel pressure is normal." << endl;}
};// 汽车类(组合引擎和车轮)
class Car 
{
private:Engine engine;   // 组合:汽车拥有引擎Wheel wheels[4]; // 组合:汽车拥有四个车轮public:void startCar() const {engine.start();for (const auto& wheel : wheels) {wheel.checkPressure();}cout << "Car is ready to drive!" << endl;}
};int main() 
{Car myCar;myCar.startCar();/* Engine starts: Vroom!Wheel pressure is normal.Wheel pressure is normal.Wheel pressure is normal.Wheel pressure is normal.Car is ready to drive! */return 0;
}

实际中尽量多使用组合,组合的耦合度低,代码维护性好。不过继承也是有用武之地的,有些关系就适合用继承,另外要实现多态也必须要继承。若是类之间的关系既可以用继承,又可以用组合,则优先使用组合。

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

相关文章:

  • 我想做个微信小程序登封搜索引擎优化
  • 网站建qq群百度关键词数据
  • 做网站的模版如何优化seo关键词
  • 如何建立网站教程优化设计六年级上册语文答案
  • 黄岩做网站百度运营平台
  • seo企业优化方案泉州seo托管
  • 沈阳 网站开发长沙专业做网站公司
  • 多语言外贸网站开发百度旗下所有app列表
  • 淘宝做网站给了钱厦门网络营销推广
  • 怎样在工商局网站上做变更线在成都网站推广公司
  • 做传奇开服一条龙网站哪个好百度seo关键词优化方案
  • 交易所网站开发实战网络营销案例分析题及答案
  • 做网站的分页查询google搜索排名优化
  • 网站开发手机号能在页面上显示吗搜索排名优化软件
  • 怎么用html做移动网站品牌推广策划方案案例
  • 网易企业邮箱邮件怎么撤回长沙seo管理
  • 荆州做网站网站建设与营销经验
  • 个人网站要怎么做网站排名分析
  • 最优的锦州网站建设北京网站推广排名
  • 网站建设相关文献苏州吴中区seo关键词优化排名
  • 手机免费永久建立网站品牌营销策划书
  • 装修素材图片都从什么网站找关键词优化排名软件流量词
  • 免费行情软件app一个seo专业学校
  • 网站开发工作怎么样成都百度推广公司联系电话
  • 拓者设计吧app网络推广seo
  • 政府网站建设 汇报惊艳的网站设计
  • nas服务器可以做网站吗百度教育官网登录入口
  • 北京网页制作模板windows优化大师收费吗
  • 白种女人做爰网站2023第二波疫情已经到来了吗
  • dede织梦织梦更换模板网站企业网站设计服务