首页 > 编程语言 >标准C++ -- day03

标准C++ -- day03

时间:2023-08-23 17:12:27浏览次数:37  
标签:函数 -- 成员 C++ day03 对象 拷贝 赋值 指针

一、 对象的创建和销毁过程分析
  1. 对象的创建过程
  • 给对象划分内存空间

  • 执行初始化列表

    • 根据继承表的顺序调用父类的无参构造或者有参构造
      • 通过:父类名(val) 调用父类的有参构造
    • 根据成员变量的定义顺序调用类类型成员的无参构造或者有参构造
      • 通过:类类型成员名(val) 调用类类型成员的有参构造
    • 对其它成员初始化
  • 执行自己的构造函数、可能会去申请资源

    class A { public: A(void) { cout << "A 的无参构造" << endl; } A(int num) { cout << "A 的有参构造" << endl;
        }
        ~A(void)
        {
            cout << "A 的析构函数" << endl;
        }
    };
    class B { public: B(void) { cout << "B 的无参构造" << endl; } B(int num) { cout << "B 的有参构造" << endl; } ~B(void) { cout << "B 的析构函数" << endl; } };
    
    class C { public: C(void) { cout << "C 的无参构造" << endl; } C(int num) { cout << "C 的有参构造" << endl; } ~C(void) { cout << "C 的析构函数" << endl; } };
    
    class D { public: D(void) { cout << "D 的无参构造" << endl; } D(int num) { cout << "D 的有参构造" << endl; } ~D(void) { cout << "D 的析构函数" << endl; } };
    
    class E:public A,public B//继承表顺序
    {
        C c;// 输出"A 的有参构造"\n "B 的有参构造"\n"C 的无参构造"\n" 的无参构造"\n"E 的无参构造"
        D d;
    public:
        E(void):A(20),B(10)// 初始化列表,会显示 "A 的有参构造"\n "B 的有参构造"\n"E 的无参构造"  顺序只与继承表顺序有关
        {
            cout << "E 的无参构造" << endl;
        }
        ~E(void)
        {
            cout << "E 的析构函数" << endl;
        }
    };
    int main()
    {
        E* e = new E; // "A 的无参构造"\n "B 的无参构造"、"E 的无参构造"
        delete e;//输出上述的逆序的析构函数
    }
    
  1. 对象的销毁过程(创建的逆序)
  • 执行自己的析构函数、可能去释放资源

  • 根据类类型成员的顺序的逆序来调用它们是析构函数

  • 根据继承表的逆序,调用父类的析构函数

  • 释放对象的内存

示例代码

class Student
{
    char name[20];
    char sex;
    int id;
public:
    Student(const char* name,char sex,int id):sex(sex),id(id)
    {
        strcpy(this->name,name);// 不能用于初始化列表中
        cout << "构造函数" << endl;
    }
    void show(void)
    {
        cout << name << " " << id << endl;
    }
}

二、 成员函数是如何区分调用它的对象 -- 使用隐藏的this指针
  1. 对象的内存只存储了成员变量,没有存储成员函数指针,相当于所有对象调用的是同一份成员函数

  2. 当对象调用成员函数时,编译器会自动把对象的地址传递给该成员函数,也就是说普通的成员函数中都有一个隐藏的参数,该参数名字叫做this指针,this指针用来接收调用对象的地址

  3. this指针拿到了调用对象的地址后,就可以直接访问该对象的成员,完成区分对象的任务

  4. 虽然this指针是隐藏定义的,但是可以显示地使用它,但不要多此一举地显式定义它

三、 常函数
  1. 被const修饰了this指针的成员函数,称为常函数

  2. 当对象调用成员函数时,编译器会隐式地把对象地址传递给成员函数

  3. 当对象被const修饰过具有常属性,就不能直接调用普通的成员函数,因为此时传递的对象地址也具有了常属性,而普通成员函数的this指针参数不具备常属性,所以编译器会报错,C++编译器不允许用带常属性的指针数据给不带常属性的指针变量赋值

  4. 因此需要让普通成员函数中的this指针也具备常属性,通过const修饰变成常函数,所以const其实修饰的是成员函数中的this指针,这样就可以让具有常属性的对象能够调用常函数了。

返回值 类名::成员函数(参数列表)const
{
    // 常函数
}
  1. 具有常属性的对象只能调用常函数,并且常函数中也只能调用常函数;不具有常属性的对象都可以调用

  2. 同名的成员函数,如果其它参数列表完全相同,但是常属性不同,也可以构成重载

  3. 正常来说在常函数中是不能修改成员变量的,除非该成员在定义时通过mutable修饰

const 在C和C++的相同点和不同点
  • 相同点:const在C和C++都是用来"显式"地保护数据不被修改

  • 不同点:

    • C++编译器会优化const变量的取值过程,即使该变量内存被强行修改,也不会改变通过变量访问的数值,这种机制会更安全。C语言编译器不会优化

    • 在C++中const还可以用于修饰成员函数的this指针,从而定义常函数

一个空结构体在C语言、C++中分别占多少字节?为什么?

  • 在C语言中空结构体占0字节

  • 在C++空结构体占1字节
    在C++结构体中可以定义成员函数,并默认有四个隐藏的成员函数(构造,析构,拷贝构造,赋值),当对象去调用成员函数时,需要传递对象的地址给成员函数,这种机制就要求结构对象需要在内存中有一席之地,所以如果结构没有任何的成员变量,编译器会让结构至少拥有1字节的不使用的内存,让上面这套机制自洽

四、拷贝构造

拷贝构造就是一种特殊版本的构造函数,格式为

类名(const 类名& that)
{
    // 执行给每个成员变量进行赋值
}

什么时候会调用拷贝构造:

  • 当使用旧对象给新对象初始化时,会自动调用拷贝构造
Test t1;                // 调用无参构造
Test t2 = t1;           // 调用拷贝构造
Test t3(t2);            // 调用拷贝构造

示例代码

class Test
{
public:
    int num;
    Test(void)
    {
        cout << "无参构造" << endl;
    }
    Test(const Test& that)
    {
        cout << "我是拷贝构造" << endl;
    }
};

拷贝对象的任务

  • 负责把旧对象中的成员变量拷贝给新对象,并且编译器默认已经自动生成具有该功能的拷贝构造函数

什么时候需要显式地写拷贝构造?

  • 普通情况下编译器自动生成的拷贝构造完全够用,但当类中成员是指针类型且为该指针成员分配了堆内存,使用默认自动生成的拷贝构造只会对指针的值进行拷贝,此时就会导致两个对象的指针成员指向同一块内存,所以在执行析构函数时会造成重复释放错误,此时应该显式地实现拷贝构造

浅拷贝和深拷贝

  • 浅拷贝:当类中的成员有指针且分配堆内存,只拷贝指针变量的值

  • 深拷贝:不拷贝指针变量的值,而是拷贝指针变量所指向的内存的内容

五、 赋值操作(拷贝赋值、赋值运算符函数)

任务:用一个旧对象给另一个旧对象赋值(两个对象都已经完成创建)

Test t1,t2;    // 无参构造
t1 = t2;       // 赋值操作操作
//注意:在C++中会把运算符当做函数处理,使用运算符时会调用运算符函数
类名& operator=(const 类名& that)
{ 

}

什么时候需要显式地写赋值

  • 类似于需要显式地写拷贝构造一样,当需要进行深拷贝时,就需要显式地写拷贝构造和赋值操作

实现赋值操作需要注意的问题:

  • 虽然赋值操作与拷贝构造的任务相同,都需要深拷贝,但是环境不同(旧对象,新对象)

  • 问题1:被赋值的对象的指针已经分配有内存

    • 先释放被赋值者指针指向的原内存

    • 根据赋值者的赋值重新申请内存

    • 把赋值者内存的内容深拷贝到新内存中

  • 问题2:可能会出现对象自己给自己赋值的情况

    • 通过判断this指针与赋值者的地址是否相同,如果相同,立即返回*this结束,如果不相同才进行赋值操作

实现一个类似于string的类 String的四个成员函数

class String
{
    char* str;
public:
    String(char* str)
    {
        this->str = new char[strlen(str)+1];
        strcpy(this->str,str);
    }
    ~String(void)
    {
        delete[] str;
    }
    String(const String& that)
    {
        str = new char[strlen(that.str)+1];
        strcpy(str,that.str);
    }
    String& operator=(const String& that)
    {
        delete[] str;
        str = new char[strlen(that.str)+1];
        strcpy(str,that.str);
        return *this;
    }
};

标签:函数,--,成员,C++,day03,对象,拷贝,赋值,指针
From: https://www.cnblogs.com/bigflyny/p/17652198.html

相关文章

  • 如何复制word的图文到百度编辑器中自动上传
    ​ 这种方法是servlet,编写好在web.xml里配置servlet-class和servlet-mapping即可使用后台(服务端)java服务代码:(上传至ROOT/lqxcPics文件夹下)<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%><%@     page contentType="text/html;cha......
  • 要搭建Redis集群高可用
    一.部署因为架构要求,只分配了两台服务器,要搭建Redis集群,为此针对两台服务器搭建了一套特殊的哨兵集群,特殊在不能无限切换,只能抗住1-2次宕机或网络故障,1-2次故障之后,集群切换机制便不能使用,需要人工按后文的步骤修复;1.前期准备准备AB两台服务器部署Redis集群,选择A服务器作为......
  • OS(十三):文件管理之外存分配方式
    文件分配外存空间时所要考虑的主要问题是:怎样才能有效地利用外存空间和如何提高对文件的访问速度。外存分配方式有连续分配、链接分配和索引分配。文件的物理结构直接与外存分配方式有关,采用不同的分配方式时,将形成不同的文件物理结构。1、连续分配1.1、连续分配......
  • IDEA ji
    MXMQUYT815-eyJsaWNlbnNlSWQiOiJNWE1RVVlUODE1IiwibGljZW5zZWVOYW1lIjoiSHVuYW4gSW5zdGl0dXRlIG9mIFNjaWVuY2UgYW5kIFRlY2hub2xvZ3kiLCJhc3NpZ25lZU5hbWUiOiJoZW51IGl0c3RhZmYiLCJhc3NpZ25lZUVtYWlsIjoiaGVudV9pdF9zdGFmZkBvdXRsb29rLmNvbSIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IkZv......
  • 树上背包优化 - 时间复杂度证明
    树上背包优化-时间复杂度证明例题树上背包顾名思义,就是在树上做背包dp树上背包的模板代码如下voiddfs(intx){ sz[x]=1; if(x>=n-m+1){ dp[x][1]=-a[x]; return; } for(PIIe:eg[x]){ intnxt=e.first; dfs(nxt); sz[x]+=sz[nxt]; for......
  • js_通过js主动触发原生事件, 以及通过js注册自定义事件并手动触发
    现实情景:在对博客园的样式进行修改时,发现需要对博客园本身的html结构进行更改,而其中一些html结构被绑定了事件处理程序.一些元素在修改时虽然被隐藏但是也需要通过其他方式来达到触发它原本绑定的事件处理程序的目的,因此需要对这些DOM的绑定事件进行手动触发主动触发......
  • vue3 用vite创建项目
    1.第一步运行创建命令(npm)npmcreatevite@latest注意的地方:首次创建的时候会出现这个 直接y就行了Needtoinstallthefollowingpackages:create-vite@latestOktoproceed?(y)直接y就行了2.第二步创建项目名称?Projectname:»vue3-demo3.第三步找到vue即可(下边有......
  • 红帽认证RedHat-RHCSA 用户和组管理
    用户和组账号概述Linux基于用户身份对资源访问进行控制用户帐号:超级用户root、普通用户、程序用户超级用户,即root用户,类似于Windows系统中的Administrator用户,非执行管理任务时不建议使用root用户登录系统普通用户帐号一般只在用户自己的宿主目录中有完全权限程序用户:用于......
  • K8S-安装笔记
    准备:主机环境的前期准备工作个人环境使用3台CentOSLinuxrelease8.5.2111,搭建需要联网,配置yum的k8s仓库等。IP地址:172.17.136.28/29/32/33,主机名对应为:gip28、gip29、gip32、gip33期中k8smaster主节点为gip28注意:以下操作如果没有特殊说明,则默认在所有的节点均执行。一、安装dock......
  • Kanban:促进价值流动的艺术-Leangoo领歌免费敏捷看板工具
    转自:本文转自Leangoo领歌敏捷工具刚接触敏捷转型的团队对Kanban和Scrum的基本概念和应用环境都不陌生,但都是作为被动接受者来理解和使用的,这篇文章的目的是希望基于底层逻辑来阐述看板的艺术来帮助团队加深理解,从而帮助ScrumMaster在带领团队的过程中灵活运用。Kanban的艺术......