C++开发基础——类对象与构造析构

news2024/11/14 15:02:03

一、基础概念

类:用户自定义的数据类型。

对象:类类型的变量,类的实例。

类的成员:成员变量和成员函数。

成员变量:类中定义的变量。

成员函数:类中定义的函数。

定义类的代码样例:

class ClassName
{
//members
};
//类定义的右花括号后面必须有分号

类的访问修饰符:public、private、protected。

public: 类的成员可以被类外部的非成员函数访问。

private: 类的成员可以被同一个类中的成员函数访问,或者被友元函数访问,该修饰符可以禁止一些针对类中成员的高风险操作。

protected: 类的成员可以在子类中被访问。

成员函数可以引用同一个类中的所有成员变量,无论它们用哪种修饰符。

class ClassName
{
public:
    //members
private:
    //members
protected:
    //members
};

C++编程中,结构体和类的使用方式几乎完全相同。结构体中的成员,可以是变量,也可以是函数。

与类的成员不同的是,结构体中的成员默认是public修饰的,而类中的成员默认是private修饰的。

访问类成员的方法: 

用类的对象来访问:对象名.成员名

用类的指针来访问:指针名->成员名

关于成员函数的内存空间:

基于同一个类创建的多个对象,该类的成员函数被多个对象所共享,即类的成员函数在多个对象之间只有一个副本。

二,构造函数

1.关于构造函数

        程序在创建对象时,将自动调用构造函数。类的成员变量可以由构造函数来初始化,构造函数与包含它的类同名,没有返回值,也没有返回类型,指定返回类型会导致编译报错。

2.默认构造函数

        如果开发者没有给类指定构造函数,编译器会给类定义一个默认的构造函数去调用,编译器生成的默认构造函数,没有参数,只创建对象,给成员变量赋默认值。

        程序中没有定义任何构造函数时,编译器会提供默认构造函数。

        当程序中已经为一个类提供了非默认构造函数,就必须再定义一个不接受任何传参的默认构造函数。

默认构造函数的定义方式: 

方式一,给已有的构造函数的所有参数提供默认值

Stock(string co = "Error", int n = 0, double price = 0.0)
{
}

方式二,通过函数重载的方式,定义一个没有任何传参的构造函数

Stock() //重载
{
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

基于默认构造函数创建对象的方式:

Stock first;
Stock first = Stock();
Stock *first_ptr = new Stock;

基于非默认构造函数创建对象的方式:

Stock first("food");

注意,调用默认构造函数,通过隐式的方式创建对象时,不要使用圆括号。

Stock second();  //返回Stock对象的函数
Stock second;  //隐式创建对象

3.构造函数的注意事项

1.不需要被显式调用,由系统调用。

2.无返回值,但是不需要用void修饰。

3.函数声明时,用public修饰。

4.对象在被复制时,调用的不是构造函数,是拷贝构造函数。

5.构造函数可以被重载,一个类可以有多个构造函数。

4.构造函数的初始化列表 

写法样例:

Time::Time(int tmphour, int tmpmin, int tmpsec)
            :Hour(tmphour), Minute(tmpmin) //初始化列表
{
    Second = tmpsec; //函数体中赋初值
}

初始化列表对变量的初始化顺序是按照变量在类中的定义顺序来操作的,先被定义的先初始化。

系统会先执行初始化列表中的初始化操作,再执行函数体中的代码逻辑。因此,可以在初始化列表中初始化成员变量的值,初始化完成后可以在函数体中修改成员变量的值。

特殊情况:const修饰的成员变量,在初始化列表中初始化以后,不能在函数体中再进行赋值操作。

Time::Time(int tmphour, int tmpmin, int tmpsec)
            :Hour(tmphour), Minute(tmpmin), testValue(30)
{
    testValue = 40; //错误操作,不可以在这里修改testValue的初值
}

5.构造函数初始化对象的方式

1、显式调用构造函数

Stock food = Stock("World Cabbage", 250, 1.25);

2、隐式调用构造函数

Stock garment("Furry Mason", 50, 2.5);  
//这种格式比较紧凑,它与下面的显式调用等价
Stock garment = Stock("Furry Mason", 50, 2.5);

3、创建对象时未提供初始值,系统会调用默认构造函数

Stock fluffy;

4、构造函数与new一起使用

Stock *pstock = new Stock("Electroshock Games", 18, 19.5);

该语句创建一个Stock对象,并将该对象的地址赋值给pstock指针。在这种情况下,对象没有名称,但可以使用指针来管理该对象。

5、特殊情况,只有一个参数的构造函数,容易发生隐式的类型转换

如果构造函数只有一个参数,则将对象初始化为一个与参数的类型相同的值时,该构造函数将被调用。

例如,构造函数原型:

Bozo::Bozo(int num)
{
    Num = num;
}

则可以使用下面的任何一种方式来初始化该对象:

Bozo A_obj = Bozo(44);
Bozo B_obj(66);
Bozo C_obj = 32;  //可以使用explicit关键字来关闭这种特性。

6.C++11风格的对象初始化

C++11中可以用{ }来进行对象的初始化:

Stock hot_tip = {"Derivatives Plus Plus", 100, 45.0};
Stock jock {"Sport Age Storage, Inc"};
Stock temp {};

三,析构函数

1.关于析构函数

类的析构函数总是在释放对象时自动调用。

如果构造函数中使用new来分配内存,则析构函数中必须使用delete来释放这些内存。

在栈内存中先后创建两个对象,最晚创建的对象将最先调用析构来删除,最早创建的对象将最后调用析构来删除。

2.析构函数的注意事项

1.不需要被显式调用,由系统调用。

2.无返回值,但是不需要用void修饰。

3.函数声明时,用public修饰。

4.析构函数没有函数参数,不能被重载,所以一个类只能有一个析构函数。

5.如果开发者在构造函数里面new了一段内存,此时需要自定义一个析构函数,并在析构函数中调用delete方法将这段内存释放掉。

对于指针类型的成员变量,在考虑析构问题时,有两个编程技巧:

忘记使用delete释放对象——使用智能指针std::unique_ptr进行封装。

不知道什么时候释放由多个对象指向或使用的同一个对象——使用智能指针std::shared_ptr进行封装。

四,创建对象:堆内存 & 栈内存

 如果对象只在一个函数中被使用,且该对象被使用的时间很短,并且从创建该对象的函数return后不再需要该对象,推荐在栈内存中创建该对象。

如果对象必须在多个函数之间使用,且该对象被使用的时间很长,推荐在堆内存中创建该对象。

栈内存中创建对象的语法:

<ClassName> <object name>;

代码样例:

Rectangle obj;

堆内存中创建对象的语法:

<ClassName> *<object name>  =  new <ClassName>;

代码样例:

Rectangle *obj = new Rectangle();

五,const对象 & const成员函数

1.const对象

const对象的所有成员变量都是const类型,不能被修改。

当通过const指针或const引用访问对象时,具有与直接访问const对象相同的限制。

对于const对象,只能调用const成员函数。

const修饰的成员函数,就是在告诉开发者,该const对象的哪些成员函数可以被调用。一般只对getter函数用const修饰,对setter函数用const修饰会导致编译报错。

2.const成员函数

只要类的成员函数不修改对象的成员变量,就应该将其声明为const。

代码样例:

void Stock::show() const    //const放函数名后面

3.mutable关键字

const对象中,被mutable关键字修饰的成员变量仍可以被修改,且可以同时被const成员函数和非const成员函数所修改。

class Screen{
public:
    void some_member() const;
private:
    mutable size_t access_ctr;  //在const对象内也能被修改
};

void Screen::some_member() const
{
    ++access_ctr;  //保存一个计数值,用于记录成员函数被调用的次数
}

六,成员函数的this指针

    this是当前对象的地址。

    返回对象本身需要进行解引用操作,即return *this,返回的是调用该成员函数的对象。

    成员函数通过this指针来访问调用它的整个对象,而不是直接访问对象的某个成员。

    正常情况下,this的类型是指向对象的常指针,const成员函数相当于把this声明为指向不可变对象的常指针。

    return this的函数返回值:返回值是对象 & 返回值是对象的引用。

    返回值是对象:改变的是同一个对象。

    返回值是对象的引用:改变的不是同一个对象,而是对象的副本。

代码样例,假设有一个对象myBox:

class Box
{
private:
    double length {1.0};
    double width {1.0};
    double height {1.0};
public:
    ...
}

Box myBox{3.0, 4.0, 5.0};

场景1:返回值是对象

成员函数:

Box* Box::setLength(double lv)
{
    if(lv > 0)
    {
        length = lv;
    }
    return this;
}

Box* Box::setWidth(double wv)
{
    if(wv > 0)
    {
        width = wv;
    }
    return this;
}

Box* Box::setHeight(double hv)
{
    if(hv > 0)
    {
        height = hv;
    }
    return this;
}

要修改同一个对象的所有成员变量,调用方法: 

myBox.setLength(20.0)->setWidth(30.0)->setHeight(40.0);
//Set all dimensions of myBox

场景2:返回值是对象的引用

成员函数:

Box& Box::setLength(double lv)
{
    if(lv > 0)
    {
        length = lv;
    }
    return *this;
}

Box& Box::setWidth(double wv)
{
    if(wv > 0)
    {
        width = wv;
    }
    return *this;
}

Box& Box::setHeight(double hv)
{
    if(hv > 0)
    {
        height = hv;
    }
    return *this;
}

要修改同一个对象的所有成员变量,调用方法:

myBox.setLength(20.0).setWidth(30.0).setHeight(40.0);
//Set all dimensions of myBox

七,关于对象的动态内存分配

1.如果对象是动态变量,则当执行完定义该对象的代码块时,就会调用该对象的析构函数。

2.如果对象是静态变量,则在整个程序运行结束时,才调用该对象的析构函数。

3.如果对象是用new创建的,则仅当显式调用delete删除对象时,才调用该对象的析构函数。

具体的区别参考《c++ primer plus》的这张图

八,对象的生命周期

1.对象的生命周期——创建对象

以下操作会创建对象:

1.在栈内存中声明对象。

2.使用new、new[]显式分配空间。

3.使用智能指针显式分配空间。

*特殊情况,创建对象的同时,创建一个内嵌的对象

#include <string>
class MyClass
{
private:
    std::string mName;
};

int main()
{
    MyClass obj;
    return 0;
}

基于MyClass类创建obj对象的时候,同时会创建一个内嵌的string对象,当包含string对象的obj对象被销毁时,string对象也被一起销毁。

2.对象的生命周期——销毁对象

销毁对象时,系统会进行的操作:调用对象的析构函数,释放对象占用的内存。

析构函数中的常见操作:释放动态分配的内存、关闭文件句柄。

对象的析构顺序与声明对象时的初始化顺序相反,最先被初始化的对象,最后被析构。

栈内存中的对象销毁:

当栈内存中的对象超出作用域以后,对象会被自动销毁。

对于一段代码,当代码遇到结束时的大括号时,这个大括号内所有创建在栈内存中的对象会被自动销毁。

例如,以下代码中,对象是创建在栈内存中的,会自动销毁。

int main()
{
    MyClass myCell("a");
    ...
    return 0;
} //myCell is destroyed as this block ends.

堆内存中的对象销毁:

如果没有使用智能指针,在堆内存中创建的对象,不会被自动销毁。

必须调用delete或delete[]删除对象指针,从而调用析构函数释放内存。

例如,以下代码中,对象是创建在堆内存中的,不会自动销毁。

int main()
{
    MyClass* objPtr1 = new MyClass("b");
    MyClass* objPtr2 = new MyClass("c");
    ...
    delete objPtr1;
    objPtr1 = nullptr;
    return 0;
} //objPtr2 is not destroyed.

九,类的静态成员

1.静态成员变量

    当类的成员变量被声明为static类型时,该变量被称为类的静态成员变量。

    类的静态成员变量作用于整个类,独立于任何类的对象。该类的所有对象都可以访问这个静态成员变量。

    静态成员变量可以作为类的特殊全局变量,它可以用来存储关于类的具体信息,比如当前类有多少个对象等。

    该类即使没有被实例化为对象,它的静态成员变量依然存在。

    静态成员变量,在多个类对象之间共享访问,只定义一次。

    在创建对象时,对象的普通成员变量会在每个对象中拷贝一个独立的副本。如果对象的某个成员变量的值是个常量,则创建多个对象还得为这个常量生成多个副本,很浪费内存空间,如果将该成员变量声明为静态成员变量,则该成员变量会被多个对象所共享,且在创建很多对象的期间只有一个实例,不会产生多个副本。

2.静态成员函数

    当类的成员函数被声明为static类型时,该函数被称为类的静态成员函数。

    类的静态成员函数也作用于整个类,独立于任何类的对象,该类的所有对象都可以调用这个静态成员函数。

    注意,由于静态成员函数与具体的对象无关,所以静态成员函数不能用const修饰,也不能使用this指针。

    如果静态成员函数被声明为public,还可以在类的外部被调用。

十,参考阅读

《C++高级编程》

《C++17入门经典》

《C++新经典》

《C++ Primer》

《C++ Primer Plus》

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

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

相关文章

社区生活超市管理系统|基于JSP技术+ Mysql+Java+ Tomcat的社区生活超市管理系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

【Unity动画】Unity如何导入序列帧动画(GIF)

Unity 不支持GIF动画的直接播放&#xff0c;我们需要使用序列帧的方式 01准备好序列帧 02全部拖到Unity 仓库文件夹中 03全选修改成精灵模式Sprite 2D ,根据需要修改尺寸&#xff0c;点击Apply 04 创建一个空物体 拖动序列上去 然后全选所有序列帧&#xff0c;拖到这个空物体…

python异常:pythonIOError异常python打开文件异常

1.python读取不存在的文件时&#xff0c;抛出异常 通过 open()方法以读“r”的方式打开一个 abc.txt 的文件&#xff08;该文件不存在&#xff09;&#xff0c;执行 open()打开一个不存在的文件时会抛 IOError 异常&#xff0c;通过 Python 所提供的 try...except...语句来接收…

Java基础经典10道题

目录 for循环的嵌套 题目一: 求101到200之间的素数的个数,并打印 代码分析: 注意点: 题目二:开发验证码 代码分析: 题目三:数组元素的复制 代码分析: 题目四:评委打分 健壮版代码: 代码分析:看源码 注意点: 题目五:数字加密 优化版代码: 代码分析: 题目六:数字…

HandyControl PropertyGrid及自定义编辑器

前提条件 项目引入对应HandyControl对应版本包。 使用案例 UI部分 <Window xmlns:hc"https://handyorg.github.io/handycontrol"><hc:TabControl><hc:TabItem Header"默认样式"><hc:PropertyGrid Width"380" SelectedO…

[RCTF2015]EasySQL ---不会编程的崽

今天也是sql注入的新类型---二次注入。不得不说花样真的多哦。 既然真的是sql注入了。那就不测试其他地方了。现在注册进去看一下界面 单纯的回显了名字。源代码里发现user.php。 可以修改密码&#xff1f;二次注入应该就在用户名这里了。因为修改密码时&#xff0c;用户名会被…

学习笔记Day11:初探Linux

Linux系统初探 Linux系统简介 发行版本Ubuntu/centOS&#xff0c;逻辑一样&#xff0c;都可以用。 服务器 本质是一台远程电脑&#xff0c;大多数服务器是Linux系统&#xff0c;通常使用命令行远程访问而不是桌面操作。LInux服务器允许多用户同时访问。NGS组学测序数据上游…

硬盘哨兵Hard Disk Sentinel Pro V6.20.0.0 便携版

Hard Disk Sentinel 是一款功能强大的硬盘监控和分析软件&#xff0c;专为 Windows 用户设计。它可以实时监测硬盘驱动器&#xff08;HDD&#xff09;、固态硬盘&#xff08;SSD&#xff09;、混合硬盘&#xff08;SSHD&#xff09;、NVMe SSD、RAID 数组和外部 RAID 盒子的健康…

pdf文件属性的删除

pdf文件属性的删除 投标过程中需要处理文件属性&#xff0c;特别是word文件属性以及pdf文件的处理 这里讲解pdf文件属性的处理 word处理在我的另外一个博客中&#xff0c;word文件属性的处理 https://ht666666.blog.csdn.net/article/details/134102504 一般用 adobe acroba…

MySQL—数据库导入篇

什么是数据库&#xff1f; 数据库是干啥的&#xff1f; 数据库&#xff08;Database&#xff09;是按照数据结构来组织、存储和管理数据的仓库。 MySQL属于哪一类数据库&#xff1f; MySQL是一种关系型数据库。所谓的关系型数据库&#xff0c;是建立在关系模型基础上的数据库&a…

LabVIEW飞行器螺旋桨性能测试与数据监控

LabVIEW飞行器螺旋桨性能测试与数据监控 开发LabVIEW的电动飞行器螺旋桨性能测试与数据监控系统&#xff0c;专门针对电动飞行器螺旋桨在运行过程中的性能测试和监控需求。通过采集转速、转矩、拉力和温度等关键参数&#xff0c;系统能够实时监测和分析螺旋桨的状态&#xff0…

深度学习_ResNet_5

ResNet学习目标 什么是ResNet为什么要引入ResNet&#xff1f;ResNet网络结构的特点利用ResNet完成图像分类 什么是ResNet&#xff1f; ResNet&#xff08;Residual Network&#xff09;是一种深度残差网络&#xff0c;由何凯明等人在2015年提出&#xff0c;是深度学习领域中一…

网络视频播放器|基于JSP技术+ Mysql+Java+ B/S结构的网络视频播放器设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

3D模型优化服务+三维可视化+数字孪生+元宇宙=眸瑞科技

眸瑞科技&#xff1a;老子云平台AMRT3D数字孪生引擎 老子云概述 老子云3D可视化快速开发平台&#xff0c;集云压缩、云烘焙、云存储云展示于一体&#xff0c;使3D模型资源自动输出至移动端PC端、Web端&#xff0c;能在多设备、全平台进行展示和交互&#xff0c;是全球领先、自…

MYSQL日志 redo_log更新流程 bin_log以及bin_log数据恢复

Redo_log写入策略 Redo log的Innodb_flush_log_at_trx_commit:: 这个参数有三个取值 取值为0&#xff1a;每次事务提交时&#xff0c;只是把redo_log留在 redo log buffer中&#xff0c;宕机会丢失数据&#xff1b; 取值为1&#xff08;默认值&#xff09;&#xff1a;每次事…

代码随想录算法训练营第day30|332.重新安排行程 、 51. N皇后 、37. 解数独

目录 332.重新安排行程 思路&#xff1a; 51. N皇后 思路&#xff1a; 37. 解数独 332.重新安排行程 力扣题目链接 (opens new window) 给定一个机票的字符串二维数组 [from, to]&#xff0c;子数组中的两个成员分别表示飞机出发和降落的机场地点&#xff0c;对该行程进…

Jenkins-pipeline流水线构建完钉钉通知

添加钉钉机器人 在钉钉群设置里添加机器人拿出Webhook地址&#xff0c;设置关键词 Jenkins安装钉钉插件 Dashboard > 系统管理 > 插件管理&#xff0c;搜索构建通知&#xff0c;直接搜索Ding Talk也行 安装DingTalk插件&#xff0c;重启Jenkins 来到Dashboard > 系…

探索编程迷宫:选择你的职业赛道

在现代科技的浪潮中&#xff0c;程序员的职业赛道就像是一座迷宫&#xff0c;充满着前端的美丽花园&#xff0c;后端的黑暗洞穴&#xff0c;以及数据科学的神秘密室。这个迷宫中&#xff0c;每一条通道都充满了挑战和机遇&#xff0c;而每一个行走其中的人都在寻找着属于自己的…

sentinel系统负载自适应流控

系统负载自适应流控 规则配置 规则创建 public class SystemRule extends AbstractRule {private double highestSystemLoad -1;private double highestCpuUsage -1;private double qps -1;private long avgRt -1;private long maxThread -1; }SystemRule类包含了以下几…

设计模式学习笔记 - 设计原则与思想总结:2.运用学过的设计原则和思想完善之前性能计数器项目

概述 在 《设计原则 - 10.实战&#xff1a;针对非业务的通用框架开发&#xff0c;如何做需求分析和设计及如何实现一个支持各种统计规则的性能计数器》中&#xff0c;我们讲解了如何对一个性能计数器框架进行分析、设计与实现&#xff0c;并且实践了一些设计原则和设计思想。当…