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

news2025/1/13 10:18:19

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

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.coloradmin.cn/o/1895805.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mmdetection3增加12种注意力机制

在mmdetection/mmdet/models/layers/目录下增加attention_layers.py import torch.nn as nn from mmdet.registry import MODELS #自定义注意力机制算法 from .attention.CBAM import CBAMBlock as _CBAMBlock from .attention.BAM import BAMBlock as _BAMBlock from .attent…

语音声控灯:置入NRK3301离线语音识别ic 掌控的灯具新风尚

一、语音声控灯芯片开发背景 我们不难发现&#xff0c;传统的灯具控制方式已难以满足现代人对便捷性和智能化的追求。传统的开关控制方式需要人们手动操作&#xff0c;不仅繁琐且不便&#xff0c;特别是在夜晚或光线昏暗的环境下&#xff0c;更容易造成不便甚至安全隐患。而语音…

Spring学习03-[Spring容器核心技术IOC学习进阶]

IOC学习进阶 Order使用Order改变注入顺序实现Ordered接口&#xff0c;重写getOrder方法来改变自动注入顺序 DependsOn使用 Lazy全局设置-设置所有bean启动时候懒加载 Scopebean是单例的&#xff0c;会不会有线程安全问题 Order 可以改变自动注入的顺序 比如有个animal的接口&a…

海外仓一件代发功能自动化:海外仓WMS系统配置方法

根据数据显示&#xff0c;2014-2019年短短几年之间&#xff0c;跨境电商销售总额增长了160%以上。这为跨境电商商家和海外仓&#xff0c;国际物流等服务端企业都提供了巨大的发展机遇。 然而&#xff0c;作为海外仓&#xff0c;要想服务好跨境电商&#xff0c;仓库作业的每一个…

JAVA进阶学习10

文章目录 一、创建不可变集合二、Stream流2.1 Stream流的获取2.1 Stream流的中间方法2.2 Stream流的终结方法 一、创建不可变集合 意义&#xff1a;如果一个集合中的数据在复制或使用过程中不能修改&#xff0c;或者被其他对象调用时不能改变内部数据&#xff0c;即增加数据的安…

【C++ 】解决 C++ 语言报错:Null Pointer Dereferenc

文章目录 引言 在 C 编程中&#xff0c;空指针解引用&#xff08;Null Pointer Dereference&#xff09;是一种常见且危险的错误。当程序试图通过空指针访问内存时&#xff0c;会导致程序崩溃或产生不可预期的行为。本文将详细探讨空指针解引用的成因、检测方法及其预防和解决…

Unity之VS脚本自动添加头部注释Package包开发

内容将会持续更新&#xff0c;有错误的地方欢迎指正&#xff0c;谢谢! Unity之VS脚本自动添加头部注释Package包开发 TechX 坚持将创新的科技带给世界&#xff01; 拥有更好的学习体验 —— 不断努力&#xff0c;不断进步&#xff0c;不断探索 TechX —— 心探索、心进取&…

【靶机实战】Apache Log4j2命令执行漏洞复现

# 在线靶场 可以通过访问极核官方靶场开启靶机实验&#xff1a;极核靶场 -> 漏洞复现靶场 -> Log4j2-RCE 原文&#xff1a;【靶机实战】Apache Log4j2命令执行漏洞复现 - 极核GetShell (get-shell.com) # 简介 Apache Log4j2 是一个广泛使用的 Java 日志记录库&#…

秋招突击——设计模式补充——简单工厂模式和策略模式

文章目录 引言正文简单工厂模式策略模式策略模式和工厂模式的结合策略模式解析 总结 引言 一个一个来吧&#xff0c;面试腾讯的时候&#xff0c;问了我单例模式相关的东西&#xff0c;自己这方面的东西&#xff0c;还没有看过。这里需要需要补充一下。但是设计模式有很多&…

比赛获奖的武林秘籍:01 如何看待当代大学生竞赛中“卷”“祖传老项目”“找关系”的现象?

比赛获奖的武林秘籍&#xff1a;01 如何看待当代大学生竞赛中“卷”“祖传老项目”“找关系”的现象&#xff1f; 摘要 本文主要分析了大学生电子计算机类比赛中“卷”“祖传老项目”“找关系”的现象&#xff0c;结合自身实践经验&#xff0c;给出了相应的解决方案。 正文 …

1-4 NLP发展历史与我的工作感悟

1-4 NLP发展历史与我的工作感悟 主目录点这里 第一个重要节点&#xff1a;word2vec词嵌入 能够将无限的词句表示为有限的词向量空间&#xff0c;而且运算比较快&#xff0c;使得文本与文本间的运算有了可能。 第二个重要节点&#xff1a;Transformer和bert 为预训练语言模型发…

百日筑基第十一天-看看SpringBoot

百日筑基第十一天-看看SpringBoot 创建项目 Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目。网址如下&#xff1a; https://start.spring.io/ 打开后的界面如下&#xff1a; 可以将 Spring Initializr 看作是 Spring Boot 项目的初始化向导&#xff…

【Unity navigation面板】

【Unity navigation面板】 Unity的Navigation面板是一个集成在Unity编辑器中的界面&#xff0c;它允许开发者对导航网格&#xff08;NavMesh&#xff09;进行配置和管理。 Unity Navigation面板的一些关键特性和功能&#xff1a; 导航网格代理&#xff08;NavMesh Agent&…

手动访问mongo和ES插入和查询

1、手动访问mongo 1.1、mongo连接数据库 1.2、mongo插入和查询 db.hmf_test.insert( { "aoeId": "1", "aoeAes": "吴秀梅", "aoeSm4": "北京xx网络技术有限公司.", "aoeSm4_a": "…

针对某客户报表系统数据库跑批慢进行性能分析及优化

某客户报表系统数据库跑批时间过长&#xff0c;超出源主库较多&#xff0c;故对其进行了分析调优&#xff0c;目前状态如下&#xff1a; 1、业务连接的rac的scanip&#xff0c;因为负载均衡将跑批的连接连接到了多个计算节点导致节点间通讯成本较高&#xff0c;故速率缓慢&…

Websocket通信实战项目(图片互传应用)+PyQt界面+python异步编程(async) (上)服务器端python实现

Rqtz : 个人主页 ​​ 共享IT之美&#xff0c;共创机器未来 ​ Sharing the Beauty of IT and Creating the Future of Machines Together 目录 项目背景 ​编辑​专有名词介绍 服务器GUI展示 功能(位置见上图序号) 客户端GUI展示&#xff08;h5cssjs&#xf…

allure如何记录操作步骤,操作步骤不写在测试用例中,同样可以体现在allure报告,如何实现

嗨&#xff0c;我是兰若&#xff0c;今天写完用例&#xff0c;在运行用例并且生成报告的时候&#xff0c;发现报告里面没有具体的操作步骤&#xff0c;这可不行&#xff0c;如果没有具体的操作步骤的话&#xff0c;用例运行失败了&#xff0c;要怎么知道问题是出现在哪一个步骤…

Android studio开发入门教程详解(复习)

引言 本文为个人总结Android基础知识复习笔记。如有不妥之处&#xff0c;敬请指正。后续将持续更新更多知识点。 文章目录 引言UITextView文本基本用法实际应用常用属性和方法 Button按钮处理点击事件 EditText输入框基本属性高级特性 ImageView图片ImageView的缩放模式 Prog…

adobe pdf设置默认打开是滚动而不是单页视图

上班公司用adobe pdf&#xff0c;自己还不能安装其它软件。 每次打开pdf&#xff0c;总是默认单页视图&#xff0c;修改滚动后&#xff0c;下次打开又 一样&#xff0c;有时候比较烦。 后面打开编辑->首选项&#xff0c; 如下修改&#xff0c;下次打开就是默认滚动了