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

马鞍山做网站公司西安今天出大事

马鞍山做网站公司,西安今天出大事,营销网站的搭建,企业网站后台模版基类与派生类之间的转换 **Day7-4 基类与派生类之间的转换****一、问题回顾****二、基类与派生类间的转换****1. 类型适应(Upcasting)****2. 逆向转换(Downcasting)** **三、代码示例****四、派生类间的复制控制****五、总结****1…

基类与派生类之间的转换

    • **Day7-4 基类与派生类之间的转换**
      • **一、问题回顾**
      • **二、基类与派生类间的转换**
        • **1. 类型适应(Upcasting)**
        • **2. 逆向转换(Downcasting)**
      • **三、代码示例**
      • **四、派生类间的复制控制**
      • **五、总结**
        • **1. `base = derived`(对象赋值,发生切片)**
          • **特点:**
        • **2. `Base& ref = derived`(引用绑定,无切片)**
          • **特点:**
        • **3. 推荐程度**
          • **更推荐的替代方案:`Base*`(指针)**
        • **4. 要点**
        • **最佳实践**

Day7-4 基类与派生类之间的转换

一、问题回顾

  1. 继承的形式有哪些?
    • 三种继承方式的特点?
    • 派生类的生成过程?
    • 继承的局限?
  2. 派生类对象的创建和销毁的特点是什么?
  3. 多继承的问题有哪些?如何解决?

二、基类与派生类间的转换

1. 类型适应(Upcasting)

在这里插入图片描述
派生类对象可以适用于基类对象,即一个派生类的实例可以用基类的引用或指针来操作。C++ 允许以下转换:

  1. 赋值转换

    Base base;
    Derived derived;
    base = derived; // 允许,但发生对象切片
    

    注意:派生类特有的数据会被丢弃。

  2. 引用转换(推荐):

    Base& ref = derived; // 允许,ref 绑定到 derived 的基类部分
    
  3. 指针转换(推荐):

    Base* pBase = &derived; // 允许,pBase 指向 derived 的基类部分
    
2. 逆向转换(Downcasting)

基类对象不能直接转换为派生类对象,除非使用强制转换

Derived* pDerived = static_cast<Derived*>(&base); // 不安全!

原因:基类对象可能不包含派生类的数据,强制转换可能导致非法访问。

使用 dynamic_cast 在多态类中更安全:

Derived* pDerived = dynamic_cast<Derived*>(&base);
if (pDerived) {// 转换成功,pDerived 可用于 Derived 类型的操作
}

三、代码示例

#include <iostream>
using namespace std;class Base {
public:Base(double base = 0.0) : _base(base) {cout << "Base(double base = 0.0)" << endl;}virtual ~Base() { cout << "~Base()" << endl; }void print() const { cout << "Base::_base = " << _base << endl; }
private:double _base;
};class Derived : public Base {
public:Derived(double base = 0.0, double derived = 0.0) : Base(base), _derived(derived) {cout << "Derived(double = 0.0, double = 0.0)" << endl;}~Derived() { cout << "~Derived()" << endl; }void show() const { cout << "Derived::_derived = " << _derived << endl; }
private:double _derived;
};int main()
{Base base(11.11);base.print();Derived derived(22.22, 33.33);derived.show();cout << "派生类向基类转换";base = derived;//1.可以将派生类对象赋值给基类对象base.print();/*与用基类对象直接初始化不同(这时会发生对象切片),引用绑定不会拷贝对象,只是为派生类对象的基类部分创建了一个别名。*/Base& ref = derived;//2.基类的引用可以绑定到派生类对象ref.print();Base* pbase = &derived;//3.基类的指针可以指向派生类对象(向上转型)pbase->print();cout << " 基类向派生类转化" << endl;Base base1(100);Derived derived1(200, 300);//derived1 = base1;//error! 1.不能将基类对象赋值给派生类对象//等价转换的形式如下:ERROR!!!//Derived& operator=(const Derived & rhs);//derived1.operator=(base1);//不存在用户定义的从 "Base" 到 "const Derived" 的适当转换//const Derived& rhs = base1;//Derived& dref = base1; //error! 2.派生类的引用不能绑定到基类对象//Derived* pref = &base1;//error! 3.派生类的指针不能指向基类对象,只指向Base基类的前8个字节,后面的指针操纵了非法内存,有内存越界的风险//语法层面不支持向下转型 基类-->派生类 ,使用强制转换(本质上是不安全的)Derived* pderived1 = static_cast<Derived*>(&base1) ;//间接的表明了pderived2指向pderived1(可以认为是安全的)Derived* pderived2 = static_cast<Derived*>(&base1);test();return 0;
}

输出示例

Base(double base = 0.0)
Derived(double = 0.0, double = 0.0)
Base::_base = 22.22
Base::_base = 22.22
~Derived()
~Base()

四、派生类间的复制控制

在继承体系中,复制控制(拷贝构造、赋值运算符)需要正确处理基类部分,否则可能导致资源泄漏或未定义行为。

要点

  1. 在拷贝构造和赋值运算符中,必须显式调用基类的对应函数,否则基类部分不会被正确复制。
  2. 避免 资源泄露,在赋值运算符中先删除旧数据,再分配新数据。
  3. 析构函数要释放动态分配的资源,否则会导致内存泄漏。
//DerivedCopyControl1.cpp
#include <iostream>
#include <string.h>using namespace std;class Base
{friend std::ostream& operator<<(std::ostream& os, const Base& rhs);
public:Base():_pbase(nullptr){cout << "Base()" << endl;}Base(const char* pbase):_pbase(new char[strlen(pbase) + 1]){cout << "Base(const char* pbase)" << endl;strcpy_s(_pbase, strlen(pbase) + 1, pbase);}Base(const Base& rhs):_pbase(new char[strlen(rhs._pbase) + 1]()){cout << "Base(const Base& rhs)" << endl;strcpy_s(_pbase, strlen(rhs._pbase) + 1, rhs._pbase);}Base& operator=(const Base& rhs){cout << "Base& operator=(const Base& rhs)" << endl;if (this != &rhs){delete[] _pbase;_pbase = nullptr;_pbase = new char[strlen(rhs._pbase) + 1]();strcpy_s(_pbase, strlen(rhs._pbase) + 1, rhs._pbase);}return *this;}~Base(){cout << "~Base()" << endl;if (_pbase){delete[] _pbase;_pbase = nullptr;}}private:char* _pbase;
};std::ostream& operator<<(std::ostream& os, const Base& rhs)
{if (rhs._pbase){os << rhs._pbase;}return os;
}class Derived : public Base
{friend std::ostream& operator<<(std::ostream& os, const Derived& rhs);
public:Derived(const char* pbase,const char* pDerived):Base(pbase),_pDerived(new char[strlen(pDerived) + 1]){cout << "Derived(const char* pbase,const char* pDerived)" << endl;strcpy_s(_pDerived, strlen(pDerived) + 1, pDerived);}Derived(const Derived& rhs):_pDerived(new char[strlen(rhs._pDerived) + 1]()){cout << "Derived(const Derived& rhs)" << endl;strcpy_s(_pDerived, strlen(rhs._pDerived) + 1, rhs._pDerived);}Derived& operator=(const Derived& rhs){cout << "Derived& operator=(const Derived& rhs)" << endl;if (this != &rhs){delete[] _pDerived;_pDerived = nullptr;_pDerived = new char[strlen(rhs._pDerived) + 1]();strcpy_s(_pDerived, strlen(rhs._pDerived) + 1, rhs._pDerived);}return *this;}~Derived(){cout << "~Derived()" << endl;if (_pDerived){delete[] _pDerived;_pDerived = nullptr;}}private:char* _pDerived;
};std::ostream& operator<<(std::ostream& os, const Derived& rhs)
{//Base& ref = rhs;//error!将 "Base &" 类型的引用绑定到 "const Derived" 类型的初始值设定项时,限定符被丢弃const Base& ref = rhs;//正确写法os << ref << "," << rhs._pDerived;return os;
}void test()
{Derived derived("hello","world");cout << "derived = " << derived << endl;cout << endl;//用一个已经存在的对象去初始化一个刚刚创建的对象Derived derived2(derived);cout << "derived = " << derived << endl;cout << "derived2 = " << derived2 << endl;cout << endl;Derived derived3("cpp", "difficult");cout << "derived3 = " << derived3 << endl;cout << endl;//两个派生类对象之间进行赋值derived3 = derived;cout << "derived = " << derived << endl;cout << "derived3 = " << derived3 << endl;
}int main() 
{test();return 0;
}

DerivedCopyControl2.cpp版本的cpp文件是为了解决上一个版本(DerivedCopyControl1.cpp)的潜在问题,主要问题如下:

  • Base的拷贝构造函数和赋值运算符未处理源对象的_pbase为nullptr的情况,导致潜在崩溃。
  • Derived的拷贝构造函数未调用Base的拷贝构造函数,导致基类部分未被正确复制。
  • Derived的赋值运算符未调用Base的赋值运算符,导致基类部分未被正确赋值。
//DerivedCopyControl2.cpp
#include <iostream>
#include <string.h>using namespace std;class Base
{friend std::ostream& operator<<(std::ostream& os, const Base& rhs);
public:Base():_pbase(nullptr){cout << "Base()" << endl;}Base(const char* pbase):_pbase(new char[strlen(pbase) + 1]){cout << "Base(const char* pbase)" << endl;strcpy_s(_pbase, strlen(pbase) + 1, pbase);}/*Base(const Base& rhs):_pbase(new char[strlen(rhs._pbase) + 1]()){cout << "Base(const Base& rhs)" << endl;strcpy_s(_pbase, strlen(rhs._pbase) + 1, rhs._pbase);}*/// 拷贝构造函数Base(const Base& rhs) : _pbase(nullptr) {cout << "Base(const Base& rhs)" << endl;if (rhs._pbase) {_pbase = new char[strlen(rhs._pbase) + 1];strcpy_s(_pbase, strlen(rhs._pbase) + 1, rhs._pbase);}}/*Base& operator=(const Base& rhs){cout << "Base& operator=(const Base& rhs)" << endl;if (this != &rhs){delete[] _pbase;_pbase = nullptr;_pbase = new char[strlen(rhs._pbase) + 1]();strcpy_s(_pbase, strlen(rhs._pbase) + 1, rhs._pbase);}return *this;}*/// 赋值运算符Base& operator=(const Base& rhs) {cout << "Base& operator=(const Base& rhs)" << endl;if (this != &rhs) {delete[] _pbase;_pbase = nullptr;if (rhs._pbase) {//base = new char[strlen(rhs._pbase) + 1]();//使用new char[size]()会进行零初始化,随后strcpy_s会覆盖这些零。此举虽无害但影响性能。_pbase = new char[strlen(rhs._pbase) + 1]; // 无需()strcpy_s(_pbase, strlen(rhs._pbase) + 1, rhs._pbase);}}return *this;}~Base(){cout << "~Base()" << endl;if (_pbase){delete[] _pbase;_pbase = nullptr;}}private:char* _pbase;
};std::ostream& operator<<(std::ostream& os, const Base& rhs)
{if (rhs._pbase){os << rhs._pbase;}return os;
}class Derived : public Base
{friend std::ostream& operator<<(std::ostream& os, const Derived& rhs);
public:Derived(const char* pbase, const char* pDerived):Base(pbase), _pDerived(new char[strlen(pDerived) + 1]){cout << "Derived(const char* pbase,const char* pDerived)" << endl;strcpy_s(_pDerived, strlen(pDerived) + 1, pDerived);}/*Derived(const Derived& rhs):_pDerived(new char[strlen(rhs._pDerived) + 1]()){cout << "Derived(const Derived& rhs)" << endl;strcpy_s(_pDerived, strlen(rhs._pDerived) + 1, rhs._pDerived);}*/// 拷贝构造函数Derived(const Derived& rhs): Base(rhs), // 调用基类拷贝构造_pDerived(new char[strlen(rhs._pDerived) + 1]) {cout << "Derived(const Derived& rhs)" << endl;strcpy_s(_pDerived, strlen(rhs._pDerived) + 1, rhs._pDerived);}/*Derived& operator=(const Derived& rhs){cout << "Derived& operator=(const Derived& rhs)" << endl;if (this != &rhs){delete[] _pDerived;_pDerived = nullptr;_pDerived = new char[strlen(rhs._pDerived) + 1]();strcpy_s(_pDerived, strlen(rhs._pDerived) + 1, rhs._pDerived);}return *this;}*/// 赋值运算符Derived& operator=(const Derived& rhs) {if (this != &rhs) {Base::operator=(rhs); // 调用基类赋值运算符delete[] _pDerived;_pDerived = new char[strlen(rhs._pDerived) + 1];strcpy_s(_pDerived, strlen(rhs._pDerived) + 1, rhs._pDerived);}return *this;}~Derived(){cout << "~Derived()" << endl;if (_pDerived){delete[] _pDerived;_pDerived = nullptr;}}private:char* _pDerived;
};std::ostream& operator<<(std::ostream& os, const Derived& rhs)
{//Base& ref = rhs;//error!将 "Base &" 类型的引用绑定到 "const Derived" 类型的初始值设定项时,限定符被丢弃const Base& ref = rhs;//正确写法os << ref << rhs._pDerived;return os;
}void test()
{Derived derived("hello", "world");cout << "derived = " << derived << endl;cout << endl;//用一个已经存在的对象去初始化一个刚刚创建的对象Derived derived2(derived);cout << "derived = " << derived << endl;cout << "derived2 = " << derived2 << endl;cout << endl;Derived derived3("cpp", "difficult");cout << "derived3 = " << derived3 << endl;cout << endl;//两个派生类对象之间进行赋值derived3 = derived;cout << "derived = " << derived << endl;cout << "derived3 = " << derived3 << endl;}int main()
{test();return 0;
}

总结:
1、如果基类实现了拷贝构造函数或赋值运算符函数,但是派生类没有实现拷贝构造函数或赋值运算符函数,那么在将一个已经存在的派生类对象初始化一个刚刚创建的派生类对象,或者将两个派生类对象进行赋值,那么派生部分会执行缺省行为,而基类部分会执行基类的拷贝构造函数或者赋值运算符函数。

2、如果基类实现了拷贝构造函数或赋值运算符函数,并且派生类也实现拷贝构造函数或赋值运算符函数,那么在将一个已经存在的派生类对象初始化一个刚刚创建的派生类对象,或者将两个派生类对象进行赋值,那么派生部分会执行派生类自己的拷贝构造函数或者赋值运算符函数,而基类部分不会自动执行基类的拷贝构造函数或者赋值运算符函数,除非显示在派生类中调用基类的拷贝与赋值。


五、总结

1. base = derived(对象赋值,发生切片)
Base base = derived;  // 对象赋值,发生切片(slicing)
base.print();         // 调用 Base::print()
特点:
  • 对象切片(Object Slicing)derived 的派生类部分被丢弃,只保留 Base 部分,base 是一个全新的 Base 对象。
  • 不推荐:除非你明确只需要基类部分,否则通常应该避免这种写法,因为它丢失了派生类的信息。

2. Base& ref = derived(引用绑定,无切片)
Base& ref = derived;  // 引用绑定,无切片
ref.print();          // 如果 print() 是虚函数,调用 Derived::print()
特点:

优点:

  1. 无对象切片ref 只是 derived 的基类部分的引用,不会复制数据,派生类信息仍然完整。
  2. 支持多态:如果 Base::print()virtual 的,调用 ref.print() 会正确调用 Derived::print()(动态绑定)。
  3. 更高效:避免了不必要的拷贝(特别是当 Base 较大时)。

缺点:

  1. 只能访问基类成员:通过 ref 无法直接访问 Derived 的特有方法(如 derived.extra())。
  2. 必须确保 derived 的生命周期:如果 derived 被销毁,ref 会变成悬空引用(dangling reference),导致未定义行为(UB)。

3. 推荐程度
写法是否推荐适用场景
Base base = derived;(赋值)❌ 不推荐除非明确只需要基类部分
Base& ref = derived;(引用)推荐需要多态、避免切片时
Base* ptr = &derived;(指针)更推荐更灵活,可以存储、传递
更推荐的替代方案:Base*(指针)
Base* ptr = &derived;  // 基类指针指向派生类对象
ptr->print();          // 多态调用 Derived::print()

优点:

  • 比引用更灵活(可以设为 nullptr,可以修改指向的对象)。
  • 常用于运行时多态(如工厂模式、容器存储不同派生类对象)。

4. 要点
  • Base& ref = derived 是推荐的,因为它避免了切片并支持多态,但必须确保 derived 的生命周期足够长。
  • Base* ptr = &derived 更推荐,因为指针更灵活,适用于更多场景(如存储在不同容器中)。
  • Base base = derived 不推荐,除非你明确只需要基类部分(但通常应该使用组合而非继承)。
最佳实践
// ✅ 推荐:指针方式(更灵活)
Base* ptr = &derived;
ptr->print();// ✅ 也可以:引用方式(适用于局部作用域)
Base& ref = derived;
ref.print();// ❌ 不推荐:赋值方式(切片问题)
Base base = derived;
base.print();

如果你需要在函数参数中传递派生类对象,通常使用 const Base&(避免拷贝)或 Base*(更灵活)。


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

相关文章:

  • 公司做网站需要准备什么材料网上交易平台
  • 人民日报客户端发稿价格宁波正规优化seo软件
  • 网站建设实训报告册网站网址查询工具
  • 网站图标按钮用什么做如何建立网页
  • 无锡市无锡市住房和城乡建设局网站代运营靠谱吗
  • 公司网站改版建议微商软文
  • 网站是广西住房和城乡建设厅沈阳百度推广排名优化
  • 建设雅马哈摩托车网站seo优化技巧
  • 广州番禺怎么读seo是指搜索引擎营销
  • 网站建设前 沟通内容天津建站网
  • 找企业做网站廊坊百度推广电话
  • 网站首页图片尺寸一站传媒seo优化
  • 手机网站可以做商城吗亚马逊关键词优化怎么做
  • 临沂网站开发公司网站seo优化报告
  • 海外代购网网站的优化seo
  • 用书籍上的文章做网站更新企业qq多少钱一年
  • 抛丸机网站怎么做域名被墙污染查询
  • 织梦移动网站网站排名查询
  • 做网站制作步骤新闻最近的新闻
  • 做购物平台网站需要多少资金软文营销文章案例
  • 网站开发与管理课程网页设计用什么软件
  • wordpress iis 安装苏州网站建设优化
  • 如何开一家网站建设公司?seo关键词排名教程
  • 广州市网站公司百度引擎搜索引擎
  • 外贸品牌网站建设网站快速优化排名方法
  • pageadmin政府网站管理系搜索引擎有哪几个网站
  • 河南省招生网站服务平台看广告赚钱一天50元
  • 哪个网站有适合小学生做的题公司网络推广网站
  • 怎么做视频的网站百度热搜seo
  • 天津圣辉友联网站建设外链发布平台大全