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

一、基础概念

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

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

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

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

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

定义类的代码样例:

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》

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

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

相关文章

带你深入了解数据库的事务

为什么要使用事务 日常开发中&#xff0c;很多操作&#xff0c;不是通过一个SQL就能完成的&#xff0c;往往需要多个SQL配合完成 当执行多个SQL操作的时候&#xff0c;如果中间出现了特殊的情况&#xff08;程序崩溃&#xff0c;系统奔溃&#xff0c;网络断开&#xff0c;主机…

大数据任务调度平台选型建议

一 背景 数仓建设过程中&#xff0c;随着业务发展&#xff0c; ETL 任务调度越来越多&#xff0c;并且这些任务的形态多种多样。怎么样让大量的 ETL 任务准确的完成调度而不出现问题&#xff0c;甚至在任务调度执行中出现错误的情况下&#xff0c;任务能够完成自我恢复甚至执行…

分治法排序:原理与C语言实现

分治法排序&#xff1a;原理与C语言实现 一、分治法与归并排序概述二、归并排序的C语言实现三、归并排序的性能分析四、归并排序的优化 在计算机科学中&#xff0c;分治法是一种解决问题的策略&#xff0c;它将一个难以直接解决的大问题&#xff0c;分割成一些规模较小的相同问…

前后端分离项目部署服务器教程--实践成功

文章目录 项目介绍流程1租界云服务2通过远程软件连接服务器3部署前后端代码停止功能文件 环境配置1.安装jdk2.安装Nginx3.安装mysql数据库 花了将近一天部署前后端的项目&#xff0c;写一个日志记录一下&#xff0c;话说孰能生巧。明天把服务器恢复初始在部署一下。 项目介绍 …

【Node.js从基础到高级运用】十四、Node.js 错误处理与日志记录

引言 在这篇博客文章中&#xff0c;我们将深入探讨Node.js中的错误处理和日志记录的最佳实践。我们会了解如何在Node.js应用程序中有效地捕获和处理错误&#xff0c;并利用日志库如morgan来记录应用程序的活动和错误信息。 第1部分&#xff1a;Node.js中的错误处理 同步代码中…

【Node.js从基础到高级运用】十三、NodeJS中间件高级应用

在现代web开发中&#xff0c;Node.js因其高效和灵活性而备受青睐。其中&#xff0c;中间件的概念是构建高效Node.js应用的关键。在这篇博客文章中&#xff0c;我们将深入探讨Node.js中间件的高级应用&#xff0c;包括创建自定义中间件、使用第三方中间件等。我们将从基础讲起&a…

CTF题型 Http请求走私总结Burp靶场例题

CTF题型 Http请求走私总结&靶场例题 文章目录 CTF题型 Http请求走私总结&靶场例题HTTP请求走私HTTP请求走私漏洞原理分析为什么用前端服务器漏洞原理界定标准界定长度 重要!!!实验环境前提POST数据包结构必要结构快速判断Http请求走私类型时间延迟CL-TETE-CL 练习例题C…

三 C#插入排序算法

简介 插入排序算法是一种简单、直观的排序算法&#xff0c;其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 插入排序实现原理 插入排序算法是一种简单、直观的排序算法&#xff0c;其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 具体实现步骤…

Java类的初始化顺序

请直接看原文: Java类的初始化顺序_java创建顺序-CSDN博客 -------------------------------------------------------------------------------------------------------------------------------- 对于静态变量、静态初始化块、变量、初始化块、构造器&#xff0c;它们的…

滴答拍摄影项目|基于Spring Boot框架+ Mysql+Java+ Tomcat的滴答拍摄影项目设计与实现(可运行源码+数据库+设计文档)

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

centos创建并运行一个redis容器 并支持数据持久化

步骤 : 创建redis容器命令 docker run --name mr -p 6379:6379 -d redis redis-server --appendonly yes 进入容器 : docker exec -it mr bash 链接redis : redis-cli 查看数据 : keys * 存入一个数据 : set num 666 获取数据 : get num 退出客户端 : exit 再退…

elk收集k8s微服务日志

一、前言 使用filebeat自动发现收集k8s的pod日志&#xff0c;这里分别收集前端的nginx日志&#xff0c;还有后端的服务java日志&#xff0c;所有格式都是用json格式&#xff0c;建议还是需要让开发人员去输出java的日志为json&#xff0c;logstash分割java日志为json格式&#…

java实现word转pdf

引入依赖包 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId…

jQuery+CSS3自动轮播焦点图特效源码

jQueryCSS3自动轮播焦点图特效源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 下载地址 jQueryCSS3自动轮播焦点图特效源码

day03vue学习

day03 一、今日目标 1.生命周期 生命周期介绍生命周期的四个阶段生命周期钩子声明周期案例 2.综合案例-小黑记账清单 列表渲染添加/删除饼图渲染 3.工程化开发入门 工程化开发和脚手架项目运行流程组件化组件注册 4.综合案例-小兔仙首页 拆分模块-局部注册结构样式完善…

LeetCode链表hard 有思路?但写不出来?

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

功能齐全的免费 IDE Visual Studio 2022 社区版

面向学生、开放源代码和单个开发人员的功能齐全的免费 IDE 下载地址 Visual Studio 2022 社区版 - 下载最新的免费版本 Visual Studio 2022 Community Edition – Download Latest Free Version 准备安装 选择需要安装的程序 安装进行中 使用C学习程序设计相关知识并培养编程…

改变input placeholder的样式 (适用于vue uniapp 中的input textarea)

如下控制 <textarea name"" placeholder"请输入您要反馈的问题&#xff0c;以便我们为您解决" placeholder-style"font-weight: 500;font-size: 27rpx;color: #999999;" id"" cols"30" rows"10"></text…

电话机器人语音识别用哪家更好精准度更高。

语音识别系统的选择取决于你的具体需求&#xff0c;包括但不限于识别精度、速度、易用性、价格等因素。以下是一些在语音识别领域表现较好的公司和产品&#xff1a; 科大讯飞&#xff1a;科大讯飞是中国最大的语音识别技术提供商之一&#xff0c;其语音识别技术被广泛应用于各…

诺视科技完成亿元Pre-A2轮融资,加速Micro-LED微显示芯片商业化落地

近日&#xff0c;Micro-LED微显示芯片研发商诺视科技&#xff08;苏州&#xff09;有限公司&#xff08;以下简称“诺视科技”&#xff09;宣布完成亿元Pre-A2轮融资&#xff0c;本轮融资由力合资本领投&#xff0c;老股东盛景嘉成、汕韩基金以及九合创投持续加码&#xff0c;这…