C++ 构造函数和析构函数

文章目录

  • 引言
  • 构造函数介绍
  • 声明和定义构造函数
  • 构造函数的使用
  • 构造函数与其他类方法的区别
  • 默认构造函数
  • 析构函数
  • C++ 11 列表初始化
  • const成员函数

引言

C++引入类的目标之一是使用类对象能像使用标准类型一样,要实现这样对目的,就必须提到C++的构造函数。如下所示的案例是int 或者结构的初始化:

int year = 2023;
struct thing
{char * pn;int m;
}
thing amabob = {"wodget", -23};

以Stock类为例,Stock的对象却不能像int类型、结构那样初始化。如:

class Stock
{
private:std::string company;long shares;double share_val;double total_val;void set_tot(){total_val * share_val;}public:...
};
Stock hot= {"Sukie's Autos, Inc", 200, 50.25}

不能像上面这样初始化Stock对象的原因是Stock的数据成员都是私有的,所以程序不能直接访问类的私有部分。如果将类的数据成员改为公有的,则破坏了类的数据隐藏(将数据封装到私有部分可以保护数据的完整性,被称为数据隐藏)。

构造函数介绍

C++提供了一个特殊的成员函数-构造函数。专门用于构造新对象、将值赋给它们的数据成员。构造函数名称与类名相同。例如Stock类的构造函数名为Stock()。构造函数的一个主要特征:没有返回值,但没有被声明为void类型。也就是说构造函数没有声明类型。

声明和定义构造函数

通常情况下,类有多少个数据成员,构造函数就有多少个参数。参数类型与类的数据成员类型保持一致。以上述的Stock类为例,Stock类有3个数据成员,则Stock类的构造函数有3个参数。可以得到Stock构造函数如下:
Stock(const string &co, long n = 0, double pr = 0.0);
第一个参数是字符串的引用,用于company成员的初始化,n和pr 参数为shares和share_val成员赋值。构造函数的原型位于类声明的公有部分(构造函数在公有部分,才能供外部程序调用)。程序在声明类对象时,会自动嗲用构造函数。

注意事项:构造哈桑农户的参数名不可以和数据成员名相同。如下所示的声明是不可以的。
Stock(const string & company, long shares, double share_val);
构造函数的参数标识的不是类的数据成员,而是赋值给类成员的值。否在在构造函数定义时会出现shares = shares;

为了避免出现shares = shares的这种混乱;有两种常见方法:
1、在数据成员名中使用m_前缀:

class Stock
{
private:string m_company;long m_shares;...
};

2、在成员名中使用后缀_

class Stcok
{
private:string company_;long shares_;...
};

以上两种类数据成员名命名方式都可以,使用其中一种即可,这样公有接口的参数就可以使用company, shares。

构造函数的使用

C++有两种使用构造函数的方法。一种是显式地调用构造函数:

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

另一种是隐式地调用构造函数:

Stock garment("Furry Mason", 50, 2.5);

这种隐式地调用格式更加紧凑,与下面显式地调用等价:

Stock garment = Stock("Furry Mason", 50, 2.5);

在创建对象时,C++都会使用构造函数。在日常的开发工作中也经常会遇到下面这种将构造函数与new一起使用的情况:

Stock *pstock = new Stock("	Electroshock Game", 18, 19.0);

注:这条语句创建了一个Stock临时对象,将其初始化为参数提供的值,并将该对象的地址赋值给pstock指针。该临时对象没有名称,但可以使用指针来管理该对象。

构造函数与其他类方法的区别

一般来说都是使用对象来调用方法。但无法使用对象来调用构造函数,因为在构造函数构造出对象之前,对象是不存在的。因此构造函数被用来创建对象,而不能通过对象来调用。

默认构造函数

默认构造函数是在未提供显式初始值时,用来创建对象的构造函数。用于以下声明方式调用的构造函数:

Stock fluffy_the_cat;//use the defalt constructor

在没有提供任何构造函数时,C++将自动提供默认构造函数,属于构造函数的隐式版本,不做任何工作。默认构造函数定义为:Stock::Stock(){ }
需要注意的是**当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。为类定义了构造函数后,程序员就必须为它提供默认构造函数。**如果提供了非默认构造函数,但没有提供默认构造函数那么直接使用Stock stock;的声明会出错。(这样做的原因可能是为了禁止创建未初始化的对象)

如果要创建对象,而不显式地初始化,则必须定义一个不接受任何参数的默认构造函数。定义默认构造函数的方式有两种。一种是给已有构造函数的所有参数提供默认值:

Stock(const string &co = "Error", int n = 0, double pr = 0.0);

另一种方式是通过函数重载来定义另一个构造函数-一个没有参数的构造函数:

Stock();

由于只能有一个默认构造函数,因此不要同时采用这两种方式。实际上通常应初始化所有的对象,以确保所有成员就有已知的合理值。用户定义的默认构造函数通常给所有成员提供隐式初始化值。如下:

Stock::Stock()
{company = "no name";shares = 0;share_val = 0;total_val = 0;
}

(注:在设计类时,通常提供对所有类成员做隐式初始化的默认构造函数)

以下列举一些常见的创建对象方式:

Stock first;//隐式地调用默认构造函数
Stock first  = Stock();//显式地调用默认构造函数
Stock *prelief =  new Stock;//隐式地调用默认构造函数
Stock first = ("Concrete Conglomerate");//调用非默认构造函数
Stock second();//隐式地调用构造函数时,不要使用圆括号。此处是声明一个返回值类型为Stock名为second的函数

析构函数

析构函数完成清理工作。如果构造函数使用new来分配内存,则析构函数将使用delete来释放这些内存。构造函数没有使用new分配内存,则析构函数也不需要使用delete。析构函数是在类名前面加上~,和构造函数一样,析构函数也没有返回值和声明类型。且析构函数没有参数。以Stock为例析构函数的原型如下:

~Stock();

对象调用析构函数的时间:
如果常见的是静态存储类对象,则其析构函数将在程序结束时自动被调用。如果创建的是自动存储类对象,则其析构函数将在程序执行完代码块(该对象在该代码块内被定义的)是自动被调用。如果对象是同通过new创建的,则它将驻留在栈内存或自由存储区中,当使用delete来释放内存时,其析构函数将自动被调用。程序可以创建临时对象来完成特定操作,在这种情况下,程序将在结束对该对象的使用时自动调用其析构函数。

改进之前介绍类和对象中涉及的Stock类
1.头文件stock10.h

#pragma once
//stock10.h--Stock class declaration with constructors, destructor added
#include <string>class Stock
{
private:std::string company;long shares;double share_val;double total_val;void set_tot() { total_val =  shares* share_val; };
public://two construtorsStock();Stock(const std::string &co, long n = 0, double pr = 0.0);~Stock();void buy(long num, double price);void sell(long num, double price);void update(double price);void show();
};

2.实现文件stock10.cpp

//stock10.cpp--Stock class with constructors,destructor added
#include "stock10.h"
#include <iostream>//constructors(verbose vesions)
Stock::Stock()//default constructor 
{std::cout << "Default constructor valled\n";company = "no name";shares = 0;share_val = 0.0;total_val = 0.0;
}Stock::Stock(const std::string& co, long n, double pr)
{std::cout << "Construtor using " << co << " called\n";company = co;if (n < 0){std::cout << "Number of shares can't be negative; " << company << " shares set to 0.\n";shares = 0;}elseshares = n;share_val = pr;set_tot();
}
//class destructor
Stock::~Stock()
{std::cout << "Bye, " << company << "!\n";
}
//other destrutor
void Stock::buy(long num, double price)
{if (num < 0){std::cout << "Number of shares purchased can't be negative. " << "Transaction is aborted.\n";}else{shares += num;share_val = price;set_tot();}
}void Stock::sell(long num, double price)
{using std::cout;if (num < 0){cout << "Number of shares sold can't be negative." << "Transaction is abored.\n";}else if (num > shares){cout << "You can't sell more than you have! " << "Transaction is aborted.\n";}else{shares -= num;share_val = price;set_tot();}
}
void Stock::update(double price)
{share_val = price;set_tot();
}
void Stock::show()
{using std::cout;using std::ios_base;//set form to #.###ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);std::streamsize prec = cout.precision(3);cout << "Company:  " << company<< " Shares: " << shares << '\n';cout<< " Share price:$" << share_val;//set format to #.##cout.precision(2);cout << " Total Worth:$" << total_val << '\n';//restore original formatcout.setf(orig, ios_base::floatfield);cout.precision(prec);
}

3.客户文件usestock1.cpp

//usestock1.cpp-- using the Stock class
//compile with stock10.cpp
#include "stock10.h"
#include <iostream>int main()
{{using std::cout;cout << "Using constrctors to creat new objects\n";Stock stock1("NanoSmart", 12, 20.0);//syntax 1stock1.show();Stock stock2 = Stock("Boffo Objects", 2, 2.0);//syntax 2stock2.show();cout << "Assigning stock1 to stock2:\n";stock2 = stock1;cout << "Listing stock1 to stock2:\n";stock1.show();stock2.show();cout << "Using a construtor to reset an object\n";stock1 = Stock("Nifty Foods", 10, 50.0);//temp objectcout << "Revised stock1:\n";stock1.show();cout << "Done\n";}
}

运行结果如下:
在这里插入图片描述

总结:构造函数不仅可以初始化新对象,还可以给已有对象赋值。

C++ 11 列表初始化

只要提供与某个构造函数的参数列表匹配的内容,并用大括号将它们括起来:

//匹配该构造函数Stock::Stock(const std::string &co, long n = 0, double pr = 0.0);
Stock hot_tip = {"Derivativew Plus Plus", 100, 45.0};
Stock jock{"Sport Age Stroage, Inc"};
//匹配默认构造函数Stock::Stock()
Stock temp{};

const成员函数

const Stock land = Stock("Kludgehorn Properties");
land.show(); //会报错

show方法无法确保调用对象不会被修改。为确保函数不会修改调用对象,C++是将const关键字放在函数的括号后面,show方法的声明和定义应如下所示:

void show() const;//声明
void Stock::show()const//定义的开头

以这种方式声明和定义的类函数被称为const成员函数。就像应尽可能将const引用和指针用作函数形参一样,只要类方法不修改调用对象,就应该将其声明为const
特别提醒:
1.如果编译器支持C++11,则可以使用列表初始化
2.接受一个参数的构造函数允许使用赋值语句将对象初始化为一个值,如下:

//构造函数原型为:Bozo(int age);
Bozo dribble = Bozo(44);
Bozo roon(66);
Bozo tubby = 32;

3.默认构造函数可以没有任何参数,如果有,则必须给所有参数都提供默认值
4.每个类都只有一个析构函数,析构函数没有返回类型(连void都没有),也没有参数,析构函数名称为类名前加上~。
5.构造函数使用了new, 则必须提供使用delete的析构函数。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 涉及opencv mediapipe知识,眨眼计数 供初学者参考

基本思路 我们知道正面侦测到人脸时&#xff0c;任意一只眼睛水平方向上的两个特征点构成水平距离&#xff0c;上下两个特征点构成垂直距离 当头像靠近或者远离摄像头时&#xff0c;垂直距离与水平距离的比值基本恒定 根据这一思路 当闭眼时 垂直距离变小 比值固定小于某一个…

Qt 输入一组数,排序后用柱状图显示

Qt柱状图&#xff0c;需要使用到QChart模块&#xff0c;因此需要在安装Qt时勾选上QChart模块。然后在工程.pro文件中加上 QT charts 参考代码&#xff1a; //MainWindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QPushButton…

基于H5“汉函谷关起点新安县旅游信息系统”设计与实现

目 录 摘 要 1 ABSTRACT 2 第1章 绪论 3 1.1 系统开发背景及意义 3 1.2 系统开发的目标 3 第2章 主要开发技术介绍 5 2.1 H5技术介绍 5 2.2 Visual Studio 技术介绍 5 2.3 SQL Server数据库技术介绍 6 第3章 系统分析与设计 7 3.1 可行性分析 7 3.1.1 技术可行性 7 3.1.2 操作…

HL 7 是什么

HL7 指的是一组用于在各种医疗服务提供者所使用之软件应用程序之间&#xff0c;传输临床和管理数据的国际标准。这些标准侧重于应用层&#xff0c;即OSI模型中的“第7层”。 HL7标准由国际标准组织Health Level Seven International制作&#xff0c;并被美国国家标准协会和国际…

智能优化算法应用:基于静电放电算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于静电放电算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于静电放电算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.静电放电算法4.实验参数设定5.算法结果6.参考…

SAP UI5 walkthrough step2 Bootstrap

我的理解&#xff0c;这就是一个引导指令 1.我们右键打开命令行--执行 ui5 use OpenUI5 2.执行命令&#xff1a;ui5 add sap.ui.core sap.m themelib_sap_horizon 执行完之后&#xff0c;会更新 yaml 文件 3.修改index.html <!DOCTYPE html> <html> <head&…

c++ redis客户端, 带详情输入输出

文章目录 使用方法输入输出解释代码输出 使用方法 g main.cpp ./a.out -h 127.0.0.1 -p 6379 输入 一行内输入 redis 命令 keys* set name get name 等等 redis命令 输出解释 输入redis: redis收到的redis协议的数据 human输入&#xff1a; 你输入的原始字符 redis输出&#xf…

如何搭建废品上门回收小程序

如今&#xff0c;随着环境保护意识的增强&#xff0c;废品的回收和再利用变得越来越重要。为了方便人们进行废品回收&#xff0c;搭建一个废品上门回收的小程序成为了一个不错的选择。本文将介绍如何从零开始搭建一个废品上门回收小程序。 …

智能优化算法应用:基于鹈鹕算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鹈鹕算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鹈鹕算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鹈鹕算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

Cisco Packet Tracer配置命令——交换机篇

交换机VLAN配置 在简单的网络环境中&#xff0c;当交换机配置完端口后&#xff0c;即可直接应用&#xff0c;但若在复杂或规模较大的网络环境中&#xff0c;一般还要进行VLAN的规划&#xff0c;因此在交换机上还需进行 VLAN 的配置。交换机的VLAN配置工作主要有VLAN的建立与删…

算术运算(这么简单?进来坐坐?)

先热热身 算术运算&#xff0c;也称为四则运算&#xff0c;包括加法、减法、乘法和除法。此外&#xff0c;算术运算还包括乘方和开方。 在算术中&#xff0c;加减被视为一级运算&#xff0c;乘除被视为二级运算&#xff0c;乘方和开方被视为三级运算。在一道算式中&#xff0c;…

佳明(Garmin) fēnix 7X 增加小睡检测功能

文章目录 &#xff08;一&#xff09;零星小睡&#xff08;二&#xff09;小睡检测&#xff08;三&#xff09;吐槽佳明&#xff08;3.1&#xff09;心率检测&#xff08;3.2&#xff09;光线感应器&#xff08;3.3&#xff09;手表重量&#xff08;3.4&#xff09;手表续航 &a…