C++(第四天----拷贝函数、类的组合、类的继承)

一、拷贝构造函数(复制构造函数)

1、概念
拷贝构造函数,它只有一个参数,参数类型是本类的引用。如果类的设计者不写拷贝构造函数,编译器就会自动生成拷贝构造函数。大多数情况下,其作用是实现从源对象到目标对象逐个字节的复制,让目标对象的每个成员变量都变得和源对象相等。编译器自动生成的拷贝构造函数称为“默认拷贝构造函数”。
2、格式

class 类名{
public:
    //普通构造函数
    类名()
    {            
    }  
    //拷贝构造函数  ,参数是本类的引用
    //如果我们没有自定义,那么编译器就自动生成默认的拷贝构造函数
    类名(const 类名 &a)
    {            
    } 
};

3、例子
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<cstring>

using namespace std;

class Student{
    
public:
    Student(const char *name,int age);//普通构造函数
    ~Student();
    
    //如果没有定义拷贝构造函数,编译器就自动生成默认拷贝构造函数--浅拷贝---值的拷贝
    /* Student(Student &a)
    {
        this->name = a->name;
        this->age = age;
    } */
    //自定义的拷贝构造函数,深拷贝
    Student(Student &a)
    {
        cout<<"Student(Student &a)"<<endl;
        
        //分配内存空间
        this->name = new char[256];
        strcpy(this->name,name);
        
        this->age = age;
    }
private:
    char *name;
    int age;
};

Student::Student(const char *name,int age=20) //name 记录的是 数据段.rodata 中的"张3" 内存空间起始地址
{
    cout<<"Student(char *name,int age=20)"<<endl;
    
    //分配内存空间
    this->name = new char[256];
    strcpy(this->name,name);
    
    this->age = age;
}

Student::~Student()
{
    cout<<"~Student()"<<endl;
    delete []this->name;
}

int main()
{
    //根据参数调用普通构造函数
    Student mya("张3");
    //如果是在定义一个对象的时候通过另一个对象来初始化,那么会调用拷贝构造函数
    //Student myb(mya);
    Student myb = mya;
    
        //打印内存空间的地址
        cout<<"mya.name:"<<static_cast<const void *>(mya.name)<<endl;
    cout<<"myb.name:"<<static_cast<const void *>(myb.name)<<endl;
    return 0;
}

4、什么时候需要自己定义拷贝构造函数
当类的数据成员中 有指针成员的时候,需要申请内存空间

5、什么时候会调用到拷贝构造函数
在定义一个对象通过另一个对象来初始化,那么会调用拷贝构造函数

6、深拷贝 和 浅拷贝
浅拷贝:只拷贝对象本身空间里面的内容
深拷贝:拷贝对象本身空间内容的同时,还要分配成员指向的堆空间并且进行拷贝

二、类的组合

1、概念
一个类的对象作为另外一个类的数据成员。
也就是说,两个及以上相互独立的类能够放在一起,然后通过一个类就可以调用另一个类的对象从而调用另一个类的功能。
2、案例
一台电脑computer和一台打印机DaYinJi两个独立的类,computer有显示、算数,DaYinJi有打印功能,当组合在一起后,对于computer类就可以调用打印机的打印功能。

#include <iostream>


using namespace std;


//打印机类
class DaYinJi{
public:
    DaYinJi(){
        cout<<"DaYinJi()"<<endl;
    }
    ~DaYinJi(){
        cout<<"~DaYinJi()"<<endl;
    }
    DaYinJi(int data):a(data)
    {
        cout<<"DaYinJi(int data)"<<endl;
    }


    int a;
    void DaYin()
    {
        cout<<"正在打印....."<<endl;
    }
};


class Demo{
public:
    Demo(int c){
        cout<<"Demo()"<<endl;
    }
    ~Demo(){
        cout<<"~Demo()"<<endl;
    }
};


//类的组合
/*
需要解决的问题??
1、在类的组合中,如何指定 内嵌对象的构造函数???   在本类的构造函数的初始化列表上指定内嵌对象的构造函数
2、构造函数的执行顺序 是  打印机类的构造函数  再到  电脑类的构造函数
*/


//电脑类
class Computer{


public:
    //在本类的构造函数的初始化列表上指定内嵌对象的构造函数
    Computer():e(2000),d(1000)
    {
        cout<<"Computer()"<<endl;
    }
    ~Computer(){
        cout<<"~Computer()"<<endl;
    }
    void exec(){
        d.DaYin();
    }
private:
    //私有成员  是 另一个类的对象   --如何去指定 对象的构造函数???
    //内嵌对象
    //如果有多个内嵌对象,那么多个内嵌对象的  构造函数的执行顺序跟  定义的先后顺序有关    跟  构造函数指定的先后顺序 无关
    DaYinJi d;
    Demo e;
};


int main()
{
    //实例化一个电脑类的对象
    Computer c1;
    //我要用电脑打印文件
    c1.exec();


    return 0;
}

3、构造函数调用顺序

在构造函数的列表中指定内嵌对象的构造函数的形式 
如果不指定内嵌对象构造函数的形式,则调用的默认构造函数 
顺序:内嵌对象 ==》本类 
多个内嵌对象 
    顺序:按照定义内嵌对象的先后顺序调用内嵌对象的构造函数

【注意】不能在构造函数的初始化列表中初始化内嵌对象成员

4、析构函数调用顺序

顺序:本类==》内嵌对象

练习1:设计一个圆形类(Circle),和一个点类(Point),计算点和圆的关系(判断该点是在圆内、圆外还是圆上)。

#include <iostream>

using namespace std;

//点类
class Point
{
public:
    //设置X坐标
    void setX(int x) {
        m_x = x;
    }
    //获取X坐标
    int getX() {
        return m_x;
    }
    //设置Y坐标
    void setY(int y){
        m_y = y;
    }
    //获取Y坐标
    int getY(){
        return m_y;
    }  
private:
    int m_x;
    int m_y;
};  
//设置一个圆类Circle
class Circle
{
public:
    //设置半径
    void setR(int r){
        m_R = r;
    }
    //获取半径
    int getR(){
        return m_R;
    }
    //设置圆心
    void setCenter(Point center) {
        m_center = center;
    }
    //获取圆心
    Point getCenter()   //m_center是Piont类的数据
    {
        return m_center;
    }  
private:
    int m_R;
    //在类中可以让另一个类 作为本类中的成员--与结构体相似
    Point m_center;
};
  
//判断点和圆的关系
void isInCircle(Circle &c, Point &p)
{
    if ((p.getX() - c.getCenter().getX()) * (p.getX() - c.getCenter().getX()) + (p.getY() - c.getCenter().getY()) * (p.getY() - c.getCenter().getY()) == c.getR() * c.getR())
        cout << "点在圆上" << endl;
    else if ((p.getX() - c.getCenter().getX()) * (p.getX() - c.getCenter().getX()) + (p.getY() - c.getCenter().getY()) * (p.getY() - c.getCenter().getY()) > c.getR() * c.getR())
        cout << "点在圆外" << endl;
    else 
        cout << "点在圆内" << endl;
}
 
int main()
{
    //创建并设置点P1
    Point P1;
    P1.setX(10);
    P1.setY(9);
 
    //创建并设置点P2--圆心
    Point P2;
    P2.setX(10);
    P2.setY(0);
 
    //设置圆C1
    Circle C1;
    C1.setR(10);
    C1.setCenter(P2);
 
    isInCircle(C1, P1);
 
    system("pause");
    return 0; 
}

三、类的继承

1、概念
新的类(子类)从已知的类(父类)中得到已有的特征的过程

新类叫派生类/子类 
已知的类叫基类/父类 

2、作用
继承可以减少重复的代码。比如父类已经提供的方法,子类可以直接使用,不必再去实现。

class 子类名:继承方式 父类(继承方式省略默认是私有继承)
{
    子类成员
};

4、例子

#include<iostream>

using namespace std;

//父类/基类
class Base{
    
public: 
    Base(){cout<<"Base()"<<endl;}
    ~Base(){cout<<"~Base()"<<endl;}
 
    void setValue(int value){
        base_a = value;
    }
    int getValue(){
        return base_a;
    }
    void showValue(){
        cout<<"base_a:"<<base_a<<endl;
    }
    int base_a;
};

class Child:public Base{

public:
    Child(){
        cout<<"Child()"<<endl;
    }
    ~Child(){
        cout<<"~Child()"<<endl;
    }    
};

int main()
{
    Child mya;
    
    //直接调用基类的函数成员
    mya.setValue(100);
    mya.showValue();
    
    return 0;
}

5、什么情况下用继承
基类和派生类之间存在is-a关系
6、总结

  • 在派生类的构造函数的初始化列表中指定基类的构造函数
  • 构造函数调用顺序:基类–》派生类
  • 析构函数调用顺序:派生类–》基类
    练习2:设计一个人的类(基类),再分别设计一个学生类(子类) 和 教师类 (子类)单继承 人类
    人类:
    属性:姓名、年龄、性别
    方法:吃饭、睡觉
    学生类:
    属性:姓名、年龄、性别、分数、学号
    方法:吃饭、睡觉、打游戏、学习
    教师类:
    属性:姓名、年龄、性别、教龄、工作类别(教的是语文还是数学还是英语)
    方法:吃饭、睡觉、学习、备课

四、继承方式
继承方式有三种: 公有继承(public)、保护继承(protected)、私有继承(private)
在这里插入图片描述
上面表格权限是基类中的成员继承到子类后的成员权限

  • 如果派生类在继承基类的时候选择 公有继承(public)
    那么基类的公有成员就是在派生类中也是充当公有成员,可以直接在派生类的内部和外部使用
    那么基类的保护成员就是在派生类中也是充当保护成员,可以在派生类的内部使用,但是不能外部使用
    那么 基类的私有成员可以继承,但是不能直接访问,可以通过基类的公有方法和保护方法间接访问

  • 如果派生类在继承基类的时候选择 保护继承(protected)
    那么基类的公有成员就是在派生类中是充当保护成员,可以在派生类的内部使用,但是不能外部使用
    那么基类的保护成员就是在派生类中是充当保护成员,可以在派生类的内部使用,但是不能外部使用
    那么 基类的私有成员可以继承,但是不能直接访问,可以通过基类的公有方法和保护方法间接访问

  • 如果派生类在继承基类的时候选择 私有继承(private)
    那么基类的公有成员就是在派生类中是充当私有成员,可以在派生类的内部使用,但是不能外部使用
    那么基类的保护成员就是在派生类中是充当私有成员,可以在派生类的内部使用,但是不能外部使用
    那么 基类的私有成员可以继承,但是不能直接访问,可以通过基类的公有方法和保护方法间接访问

#include<iostream>

using namespace std;

//基类
class Base{
    
public:
        Base(int data=100):baseData(data){
            cout<<"Base()"<<endl;
        }
        ~Base(){
            cout<<"~Base()"<<endl;
        }
        void setData(int data){
            baseData = data;
        }
        int getData(){
            return baseData;
        }
protected:
        void showData(){
            cout<<"baseData:"<<baseData<<endl;
        }
private:
        int baseData;
};

class Child:private Base{
    
public:
        Child(int data=20):Base(data){
            showData();
            cout<<"Child()"<<endl;
        }
        ~Child(){
            cout<<"~Child()"<<endl;
        }
};
int main()
{
    Child mya(200);
    
    //mya.setData(1000);
    
    return 0;
}

四、总结:

类的组合(has-a)和类的继承(is-a)是面向对象编程中两种重要的关系,它们在概念、使用场景、实现方式等方面存在显著的区别。以下是这两者的详细对比:

一、概念区别
类的组合(has-a):
指的是一个类(或对象)包含另一个类的对象作为其属性。这种关系强调的是“拥有”或“包含”的关系。
例如,一个汽车类可以包含一个发动机类的对象作为属性,表示汽车拥有发动机。
类的继承(is-a):
指的是一个类(子类)继承另一个类(父类)的属性和方法。这种关系强调的是“是一种”的关系。
例如,一个猫类可以继承一个动物类,表示猫是动物的一种。
二、使用场景区别
类的组合:
当一个类需要复用另一个类的功能,但又不希望与其产生过于紧密的联系时,使用组合。
组合关系有助于降低类之间的耦合度,提高系统的灵活性和可维护性。
例如,在设计一个图形界面时,一个窗口类可以包含多个按钮类的对象,而不需要通过继承来实现。
类的继承:
当一个类需要继承另一个类的属性和方法,并且这些属性和方法对于子类来说具有普遍意义时,使用继承。
继承关系有助于实现代码的重用和扩展,但也可能导致类之间的耦合度过高,增加系统的复杂性。
例如,在设计一个动物类库时,可以使用继承来定义不同种类的动物,如猫、狗等,它们都继承自动物类。
三、实现方式区别
类的组合:
在组合类中,通过属性来持有另一个类的对象。
可以通过这个属性来调用被包含类的方法,但不需要继承其所有方法和属性。
组合关系通常是在运行期确定的,因为可以在运行时动态地创建和销毁被包含类的对象。
类的继承:
在继承关系中,子类继承父类的所有非私有属性和方法(在Java等语言中,私有属性和方法不能被继承)。
子类可以覆盖(Override)父类的方法,以提供自己的实现。
继承关系在编译期就已经确定,因为子类需要知道父类的所有接口才能正确地实现它们。
四、其他区别
耦合度:组合关系是一种松耦合关系,而继承关系则是一种紧耦合关系。松耦合关系有助于降低系统各部分之间的依赖程度,提高系统的灵活性和可维护性。
多态性:在继承关系中,可以实现类型的回溯(即子类对象可以被当作父类对象来使用),从而实现多态性。而在组合关系中,通常不具备这种特性。
设计原则:在面向对象设计中,通常建议优先考虑组合而不是继承。因为组合关系更加灵活,可以更好地应对变化。而继承关系则可能导致系统结构过于复杂和僵化。
综上所述,类的组合和类的继承在面向对象编程中各有其独特的优势和适用场景。在实际应用中,应根据具体需求和设计原则来选择合适的关系来实现代码的组织和复用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/772715.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Access,Trunk,Hybrid网络设备链接类型详解

带着问题找答案&#xff1a;网络链路上的数据包怎么看&#xff0c;是否携带vlan-id如何看&#xff0c;以及如何设计链接类型满足用户要求&#xff0c;请看如下解析。 第一种&#xff1a;链接类型access 无标记数据帧 第二种&#xff1a;链接类型trunk 第三种&#xf…

最新mysql打开远程访问和修改最大连接数

这里写目录标题 1.使用navicat进入命令控制板,进入use mysql;2.查询用户表3.更新user表中root用户域属性&#xff0c;%表示允许外部访问4.执行以上语句之后再执行&#xff0c;FLUSH PRIVILEGES;5. 执行授权语句修改最大连接数 1.使用navicat进入命令控制板,进入use mysql; use…

为什么写Python脚本时要加上if __name__ == ‘__main__‘?

目录 一、__name__ 的秘密 二、if __name__ __main__: 的作用 三、代码示例与案例分析 示例一&#xff1a;简单的数学工具模块 示例二&#xff1a;命令行工具 四、实际应用场景 五、进阶应用 1. 插件开发 2. 动态加载模块 3. 交互式与脚本模式切换 六、结论 在Pyth…

电商API对接流程丨从零开始快速打通电商平台数据通道

开发电商业务管理系统时&#xff0c;怎么对接电商接口呢&#xff1f;有两种方式可供选择&#xff0c;一种方式就是自己入驻想要对接的电商平台对应的开放平台&#xff0c;按照要求与流程与电商接口进行对接&#xff0c;还有一种方式就是寻找电商中台&#xff0c;通过第三方接口…

吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.5-2.6

目录 第三门课 结构化机器学习项目&#xff08;Structuring Machine Learning Projects&#xff09;第二周&#xff1a;机器学习策略&#xff08;2&#xff09;(ML Strategy (2))2.5 数据分布不匹配时的偏差与方差的分析&#xff08;Bias and Variance with mismatched data di…

玩机进阶教程----MTK芯片杂牌机 小品牌机型解除bl锁以及root的操作步骤解析

在玩机过程中会遇到很多小品牌机型或者杂牌机类的。大多都使用mtk芯片。而且基本很少有官方线刷包。在这些机型中玩机首先我们要想办法导出系统来制作线刷包。以免后续解锁bl或者root出现未知故障可以恢复原系统。 那么对于这些机型该如何进行备份固件和root呢。通过博文可以初…

选微调、RAG还是微调+RAG?

RAG技术是一种结合了检索与生成的方法。它通常依赖于两个核心组件&#xff1a;一个大型语言模型&#xff08;如GPT-3&#xff09;和一个检索系统&#xff08;如向量数据库&#xff09;。RAG先使用检索系统从大量数据中检索出相关信息&#xff0c;然后将这些信息提供给语言模型&…

一文带你看懂什么是营销归因模型及SaaS企业的应用

在数字化时代&#xff0c;营销活动的多样性和复杂性使得评估其效果成为一项挑战。营销归因模型应运而生&#xff0c;为SaaS企业等提供了科学、系统的评估工具。本文将简要介绍什么是营销归因模型&#xff0c;阐述其带来的好处&#xff0c;并探讨SaaS企业可以采用的营销归因系统…

编译rust程序,并让它依赖低版本的GLIBC库

在linux环境下编译rust程序&#xff0c;编译好的程序会依赖你当前系统的GLIBC库&#xff0c;也就是说你的程序无法在使用更低版本GLIBC库的linux系统中运行。 查看当前系统的GLIBC版本&#xff1a; strings /lib64/libc.so.6 | grep GLIBC 为了让编译的程序依赖比较低版本的GL…

通过 Power Automate 以提升的权限运行 Power Apps 连接

使用Power Apps在Sharepoint列表中新建或编辑项比较简单&#xff0c;就是创建窗体&#xff0c;连接Sharepoint列表&#xff0c;添加个按钮&#xff0c;触发条件为Submit(form)。 填写信息&#xff0c;点击按钮即可新建项 但使用过程中&#xff0c;发现运行此应用的用户&#xf…

mac 11 变编译安装nginx

mac 11 变编译安装nginx 记录一次安装过程 所需要的包 pcre: https://sourceforge.net/projects/pcre/files/pcre/OpenSSL: https://www.openssl.org/source/Nginx: https://nginx.org/en/download.html如果没有pcre 和Openssl,报错如下 把pcre和Openssl 解压到nginx 目录下…

MySQL数据库的备份-恢复-日志

一、备份 1.1数据备份的重要性 备份的主要目的是灾难恢复。 在生产环境中&#xff0c;数据的安全性至关重要。 任何数据的丢失都可能产生严重的后果。 1.2造成数据丢失的原因 程序错误人为操作错误运算错误磁盘故障灾难&#xff08;如火灾、地震&#xff09;和盗窃 1.3数…

Altium Designer专业PCB设计软件下载安装 Altium Designer安装包下载获取

在电子设计的广袤领域中&#xff0c;PCB设计无疑占据着重要的地位。而Altium Designer作为一款业界领先的电子设计自动化软件&#xff0c;其提供的先进布局工具&#xff0c;无疑为设计师们打开了一扇通往高效、精确设计的大门。 在PCB设计的核心环节——布局中&#xff0c;Alti…

【苍穹外卖】Day1遇到的问题

1、lombok版本不兼容问题 java: java.lang.IllegalAccessError: class lombok.javac.apt.LombokProcessor (in unnamed module 0x3278991b) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module jdk.comp…

文心一言 VS 讯飞星火 VS chatgpt (295)-- 算法导论21.4 4题

四、利用练习 21.4-2 &#xff0c;请给出一个简单的证明&#xff0c;证明在一个不相交集合森林上使用按秩合并策略而不使用路径压缩策略的运行时间为 O(m lgn) 。21.4-2 的内容是&#xff1a;“证明&#xff1a;每个结点的秩最多为 ⌊lgn⌋ 。”。如果要写代码&#xff0c;请用…

CenterOS7安装java

CenterOS7安装java #进入安装目录 cd /usr/local/soft/java#wget下载java8 #直接进入官网选择相应的版本进行下载&#xff0c;然后把下载链接复制下来就可以下载了 #不时间的下载链接不一样 wget http://download.oracle.com/otn-pub/java/jdk/8u181-b13/96a7b8442fe848ef90c9…

Java的编程之旅46——List集合

1.List集合的特殊方法 List接口是Collection接口的子接口&#xff0c;提供了一系列操作元素的方法。 常见的List集合实现类有ArrayList和LinkedList。ArrayList我们在前面已经介绍过了&#xff0c;这一章中着重介绍一下List集合的特有方法。 List<String> list new Arr…

第3章.中央服务器的物联网模式--规则引擎

规则引擎 规则引擎本质上是物联网事件和需要与这些事件相关联的动作之间的映射。在物联网环境中&#xff0c;事件通常使用传感器生成&#xff0c;所需的动作由执行器采取。本书中用于该图案的符号如下图所示&#xff1a; 图3.6–“规则引擎”模式的符号 一个有趣的类比是将规则…

Linux 摄像头编号固化

一、前言 在工业领域&#xff0c;一台设备会有很多个摄像头&#xff0c;可以使用命令&#xff1a;ll /dev/video* 进行查看&#xff1b; 在代码中&#xff0c;如果需要使用摄像头&#xff0c;那么都是需要具体到哪个摄像头编号的&#xff0c;例如 open("/dev/video4"…

数据库数据插入全解析:语法与示例

目录 一、基础数据插入 &#x1f38a;基本语法 &#x1f384;示例 二、使用 SET 进行数据插入 &#x1f697;基本语法 &#x1f3a2; 示例 三、插入查询结果 &#x1f953;基本语法 &#x1f95e;示例 四、插入并获取自增ID &#x1f68d;示例&#xff08;MySQL&…