C++中的指针类型与构造函数,析构函数

article/2025/9/14 4:10:34

1. 指针类型的作用

1.1 指针取出字节

  • 任何类型的指针占用的空间大小都是相同的
    (32位CPU是4字节;64位CPU是8字节)

  • 既然任何类型的指针占用的空间大小都是相同的,为什么指针还需要类型呢?

  • 指针只是指向了一个内存地址,但是当存内存中取值的时候,系统不知道你要从当前指针指向的地址,取几个字节,指定了指针的类型后,系统就知道取几个字节了。

  • char类型取1个字节,short类型取2个字节,int类型去4个字节。

注意:

  1. bit 是计算机最小的单位; 一个字节 = 8 bit;

  2. 一个十六进制符 需要四位才能表示出来;
    0xf: 十六进制中的 一个16进制数, 比如 f , 需要用四位 二进制才能表示处理;
    十六进制数 f f f : 需要四位表示出来: 1111;

所以一个十六进制符占用的是 半个字节,
两个十六进制符 才是占用的一个字节;

* char字符存储空间为一个字节,* 16进制的每个字符需要用4位二进制位来表示,* 0x0为0000,0xf为1111,即1个16进制数为4位,* 如42 4D 38 04 04 00 00 00 00 00 36 04 00 00,每两个16进制数隔开,* 用意是:因为1个16进制数为4位,两个就是8位,
所以两个16进制数 占用8位, 占用了一个* 即1个字节,所以这里是14字节,,以字节为单位,容易计数
int main() {int i = 123456789;//i的值用16进制表示就是:0x075bcd15int *p = &i;printf("int:%d\n", *p);//打印出123456789//char *c = (char*)&i;char *c;c = (char*)&i;//打印出21,因为是char类型的指针,所以取出1个字节,也就是去的是16进制的15,十进制就是21.printf("char:%d\n", *c);//打印出-13035,因为是short类型的指针,所以取出2个字节也就是去的是16进制的cd15,十进制就是-13035short *s = (short*)&i;printf("short:%d\n", *s);}

在这里插入图片描述

不同的指针类型,可以指示编译器怎样解释特定地址上内存的内容以及该内存区域应该跨越多少内存单元。

如果一个int 型的指针:
寻址到1000 内存处,
那么在32 位机器上跨越的地址空间是1000~1003;

如果一个double 型的指针:
寻址到1000 内存处,
那么在32 位机器上跨越的地址空间是1000~1007;

1.2 指针的定义

指针p也是对象,它同样有地址&p和存储的值p,
只不过,p存储的数据类型是数据的地址。

指针在定义的时候:
声明该变量, 是指针类型的变量:

  int i = 3;int* p = &i

但是在使用的时候, 称作是 解地址符: 意思是取出该指针中所保存的地址1, 找到该地址1, 取出地址1中的值;

 int value_i = *p cout<< value_i<< endl;

2. 构造函数

构造函数是指一个特殊的公共成员函数, 它的作用是在创建类的对象时会自动调用,从而用于构造类的对象;

构造函数的特点:

  1. 是指它的函数名称 与所属类的名称相同, 从而编译器知道这是类中一个特殊的成员函数;

  2. 构造函数不允许有返回类型, 除此之外, 构造函数和其他类的成员函数相似;

  3. 有个特点, 通常类名称首字母大写, 所以构造函数的首字母也会大写;

从而函数名称与类名称是否相同, 函数名称首字母是否大写, 是否有无函数返回类型, 这三点,可以区分普通成员函数和构造函数;

所以,我们程序员在创建类的时候, 我们应该自己定义出构造函数;
在自己定义了构造函数后,
好处是,在初始化类的对象时, 可以直接对类中的成员变量赋值;

若没有自定义构造函数,调用系统自动生成的构造函数时, 在初始化对象时,不可以直接对类中的成员变量赋值;

如果程序员没有编写构造函数,则 C++ 会自动提供一个,这个自动提供的构造函数永远不会有人看到它,但是每当程序定义一个对象时,它会在后台静默运行。

构造函数的类型:

  1. 有形参的普通自定义构造函数;
  2. 没有形参的 的默认构造函数;
  3. 有形参的默认构造函数, 但是形参都设置了默认指, 无实参可以调用;

注意,一般构造函数可以有多种参数形式,
一个类可以有多个普通构造函数,前提是参数的个数或者类型不同(C++的函数重载机制)

但是, 默认构造函数只能有且只有一个

程序员通常使用构造函数来初始化对象的成员变量。但实际上,它可以做任何正常函数可以做的事情。

举个例来说明有 无自定义构造函数区别:
单链表中的构造函数:

struct ListNode{
int val;ListNode *next;
// 节点的构造函数
ListNode (int x): val(x), next(NULL){}
};

因为上述结构体中, 自己定义了 构造函数:

所以在初始化的时候, 可以直接给变量赋值;

ListNode* head = new ListNode(5);

而如果没有自己定义构造函数, 则c++ 默认生成一个构造函数,
但是 使用默认生成的构造函数时, 在初始化的时候,不可以给变量赋值;

2.1 在类中定义构造函数

下面的程序包括一个名为 Demo 的类,其构造函数除了打印消息之外什么都不做。编写它的目的就是为了演示构造函数何时执行。因为 Demo 对象是在两个 cout 语句之间创建的,所以构造函数将在这两个语句生成的输出行之间打印它的消息。

// This program demonstrates when a constructor executes.
#include <iostream>
using namespace std;
class Demo
{public:Demo(){cout << "Now the constructor is running.\n";}
};
int main()
{cout << "This is displayed before the object is created. \n";Demo demoObj;    // Define a Demo objectcout << "This is displayed after the object is created.\n";return 0;
}

程序中,将构造函数定义为类声明中的内联函数;

程序输出结果为:
This is displayed before the object is created.
Now the constructor is running.
This is displayed after the object is created.

2.2 在类外定义构造函数

当然,像任何其他类成员函数一样,也可以将其原型放在类声明中,然后将其定义在类之外。

在这种情况下,需要添加:

  1. 函数所属类的名称和
  2. 函数名前面的作用域解析运算符。

但是由于构造函数的名称与类名相同,所以名称会出现两次。

Demo:: Demo ()    // 构造函数
{cout << "Now the constructor is running. \n";
}

2.3 重载构造函数

我们知道,当两个或多个函数共享相同的名称时,函数名称被称为重载。只要其形参列表不同,C++ 程序中可能存在具有相同名称的多个函数。

任何类成员函数都可能被重载,包括构造函数。例如,某个构造函数可能需要一个整数实参,而另一个构造函数则需要一个 double,甚至可能会有第三个构造函数使用两个整数。只要每个构造函数具有不同的形参列表,则编译器就可以将它们分开。

下面的程序声明并使用一个名为 Sale 的类,它有两个构造函数。第一个构造函数的形参接受销售税率;第二个构造函数是免税销售,没有形参。它将税率设置为 0。这样一个没有形参的构造函数称为默认构造函数。

#include <iostream>
#include <iomanip>
using namespace std;
// Sale class declaration
class Sale
{private:double taxRate;public:Sale(double rate) // Constructor with 1 parameter{taxRate = rate; // handles taxable sales}Sale ()    // Default constructor{taxRate = 0.0    // handles tax-exempt sales}double calcSaleTotal(double cost){double total = cost + cost*taxRate;return total;}
};
int main()
{Sale cashier1(.06); // Define a Sale object with 6% sales taxSale cashier2;    // Define a tax-exempt Sale object// Format the outputcout << fixed << showpoint << setprecision (2);// Get and display the total sale price for two $24.95 salescout << "With a 0.06 sales tax rate, the total of the $24.95 sale is $ \n";cout << cashier1.calcSaleTotal(24.95) << endl;cout << "\n On a tax-exempt purchase, the total of the $24.95 sale is, of course, $\n";cout << cashier2.calcSaleTotal(24.95) << endl;return 0;
}

输出结果:

With a 0.06 sales tax rate, the total of the $24.95 sale is $26.45On a tax-exempt purchase, the totalof the $24.95 sale is, of course, $24.95

注意看此程序如何定义的两个 Sale 对象:

Sale cashier1(.06);
Sale cashier2;

在 cashier1 的名称后面有一对括号,用于保存值,发送给有一个形参的构造函数。但是,在 Cashier2 的名称后面就没有括号,它不发送任何参数。在 C++ 中,当使用默认构造函数定义对象时,不用传递实参,所以不能有任何括号,即:

Sale cashier2 () ;    // 错误
Sale cashier2;    // 正确

构造函数和默认构造函数的 区别是:
当类的对象创建时, 但没有给对象的成员变量 初始化赋值时,
则编译器,会自动调用默认构造函数;

通常情况下,默认构造函数没有形参;

2.3 无形参的默认构造函数

为了创建不传递任何参数的对象,必须有一个不需要参数的构造函数,也就是默认构造函数。

如果没有这样一个默认构造函数,那么当程序尝试创建一个对象而不传递任何参数时,它将不会编译,这是因为必须有一个构造函数来创建一个对象

如果程序员没有为类编写任何构造函数,则编译器将自动为其创建一个默认构造函数。

但是,当程序员编写了一个或多个构造函数时,即使所有这些构造函数都是有参数的,编译器也不会创建一个默认构造函数,所以程序员有责任这样做。

那么,在设计一个具有构造函数的类时,应该包括一个默认构造函数,这在任何时候都会被认为是一个很好的编程实践。

类可能有许多构造函数,但只能有一个默认构造函数。
这是因为:如果多个函数具有相同的名称,则在任何给定时刻,编译器都必须能够从其形参列表中确定正在调用哪个函数。
它使用传递给函数的实参的数量和类型来确定要调用的重载函数

因为一个类名称只能有一个函数能够接受无参数,所以只能有一个默认构造函数。

2.3 有形参的默认构造函数

一般情况下,就像在 Sale 类中那样,默认构造函数没有形参。

Sale 类需要一个默认构造函数来处理免税销售的情况,但是其他类可能并不需要这样一个构造函数。

例如,如果通过类创建的对象总是希望将实参传递给构造函数。

所以有另一种的默认构造函数,其所有形参都具有默认值,所以,它也可以无实参调用。

如果创建了一个接受无实参的构造函数,同时又创建了另外一个有参数但允许所有参数均为默认值的构造函数,那么这将是一个错误,因为这实际上是创建了两个“默认”构造函数。以下语句就进行了这种非法的声明:

class Sale //非法声明,  不应该存在两个 默认构造函数;
{private:double taxRate;public:Sale()    //无实参的默认构造函数{taxRate = 0.05;}Sale (double r = 0.05)    //有默认实参的默认构造函数{taxRate = r;}double calcSaleTotal(double cost){double total = cost + cost * taxRate;return total;};
};

可以看到,第一个构造函数没有形参,第二个构造函数有一个形参,但它有一个默认实参。如果一个对象被定义为没有参数列表,那么编译器将无法判断要执行哪个构造函数。

2.4 各自构造函数的写法

2.4.1 有形参的普通构造函数

具有形参的普通构造函数,可以有两种写法:

  1. 初始化列表的方式:

构造函数名称( 初始化值1, 初始化值2 ):成员变量1(初始化值1),… 成员变量n(初始化值n){}

class Student{
public:
int m_age;
int m_score;// 列表方式,定义构造函数;
Student(int age, int score): m_age(age), m_socre(score){}};
  1. 内部赋值方式:
    正常函数的赋值
Student(int age, int score){m_age = age;m_score = score;
}

C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。也就是说采用初始化列表的话,构造函数本体实际上不需要有任何操作,因此效率更高。

2.4.1 无形参的构造函数

函数内部赋值

Student(){m_age = 0;m_score = 0;
}

3. 拷贝构造函数

拷贝构造函数 为类对象本身的引用,
根据一个已经存在的对象复制出一个新的对象,一般在函数中,会将已经存在对象的数据成员的值复制一份到新创建的对象中;


Student(Student& S){m_age = s.m_age;m_score = s.m_score;cout << " 这是个 复制构造函数" << endl;}

注意:若没有显示定义复制构造函数,则系统会默认创建一个复制构造函数,当类中有指针成员时,由系统默认创建的复制构造函数会存在“浅拷贝”的风险,因此必须显示定义复制构造函数。

  • 浅拷贝指的是在对对象复制时,只对对象中的数据成员进行简单的赋值,若存在动态成员,就是增加一个指针,指向原来已经存在的内存。这样就造成两个指针指向了堆里的同一个空间。当这两个对象生命周期结束时,析构函数会被调用两次,同一个空间被两次free,造成野指针。

  • 深拷贝就是对于对象中的动态成员,不是简单的赋值,而是重新分配空间。

4. 析构函数

析构函数是具有与类相同名称的公共成员函数,前面带有波浪符号(〜)。例如,Rectangle 类的析构函数将被命名为 〜Rectangle。

当对象被销毁时,会自动调用析构函数。在创建对象时,构造函数使用某种方式来进行设置,那么当对象停止存在时,析构函数也会使用同样的方式来执行关闭过程。

4.1 析构函数的特点

除了需要知道在对象被销毁时会自动调用析构函数外,还应注意以下事项:

  • 像构造函数一样,析构函数没有返回类型。
  • 析构函数不能接收实参,因此它们从不具有形参列表。
  • 由于析构函数不能接收实参,因此只能有一个析构函数。

例如,当具有对象的程序停止执行或从创建对象的函数返回时,就会发生这种情况。

下面的程序显示了一个具有构造函数和析构函数的简单类。它说明了在程序执行过程中这两个函数各自被调用的时间:

//This program demonstrates a destructor.
#include <iostream>
using namespace std;
class Demo
{public:Demo(); // Constructor prototypeDemo(); // Destructor prototype
};
Demo::Demo()    // Constructor function definition
{cout << "An object has just been defined,so the constructor" << " is running.\n";
}
Demo::Demo() // Destructor function definition
{cout << "Now the destructor is running.\n";
}
int main()
{Demo demoObj;    // Declare a Demo object;cout << "The object now exists, but is about to be destroyed.\n";return 0;
}
程序输出结果:
An object has just been defined, so the constructor is running.
The object now exists, but is about to be destroyed.
Now the destructor is running.

http://chatgpt.dhexx.cn/article/sf3xLtaG.shtml

相关文章

c++拷贝构造函数

一. 什么是拷贝构造函数 首先对于普通类型的对象来说&#xff0c;它们之间的复制是很简单的&#xff0c;例如&#xff1a; int a 100; int b a; 而类对象与普通对象不同&#xff0c;类对象内部结构一般较为复杂&#xff0c;存在各种成员变量。 下面看一个类对象拷贝的简单…

C++构造函数

文章目录 1 构造函数的定义及特征2 默认构造函数2.1 合成的默认构造函数2.1.1 默认初始化2.1.2 合成的默认构造函数初始化类成员变量的规则2.1.3 有些场合不能使用合成的默认构造函数 2.2 全缺省的默认构造函数&#xff08;参数都为默认实参的构造函数&#xff09; 3 构造函数初…

C++ 构造函数详解

目录 0. 什么是构造函数 1. 默认构造函数 2. 一般构造函数 3. 拷贝构造函数 4. 转换构造函数 5. 移动构造函数 0. 什么是构造函数 在定义类的成员函数的时候&#xff0c;一般的成员函数与普通的全局函数没有太大的区别&#xff0c;在定义函数的时候都需要说明要返回的类…

【C++要笑着学】类的默认成员函数详解 | 构造函数 | 析构函数 | 构造拷贝函数

&#x1f525; &#x1f525; &#x1f525; &#x1f525; &#x1f525; 火速猛戳订阅 &#x1f449; 《C要笑着学》 &#x1f448; 趣味教学博客 &#x1f525; &#x1f525; &#x1f525; &#x1f525; &#x1f525; [ 本篇博客热榜最高排名&#xff1a;7 ] 写在前…

JSF 文档参考

转自&#xff1a;http://blog.csdn.net/ontheway20/article/details/38532241 A4J 用户指南 目录 1. 介绍 2. 开始使用Ajax4jsf 环境需求 下载Ajax4jsf 安装 简单的 AJAX Echo 项目 JSP 页面 数据 Bean faces-config.xml Web.xml 部署 3. Ajax4jsf 框架的基本概念 介绍 结构概…

IDEA 2020.2 部署JSF项目

目录 一、用Glassfish部署JSF项目 1、下载glassfish 2、配置glassfish环境变量 3、修改jdk环境变量 4、测试glassfish是否可以正常启动 5、在IDEA中创建一个JSF项目 6.问题&#xff1a;部分标签元素无法显示 二、用tomcat部署JSF项目 1、新建项目或者模块&#xff0c;…

JSF教程(1)——简介 + HelloWorld

在写第一个HelloWorld之前先来宏观的了解一下JSF&#xff0c;也许你之前使用过Struts&#xff08;1或者2&#xff09;&#xff0c;SpringMVC&#xff0c;甚至于直接采用JSPServelet开发过web层。JSF与这些最大的不同是JSF是基于一种以组件为中心的用户界面&#xff08;UI&#…

JSF程式

概述&#xff1a; jsf使用spring的依赖注入的思想使得页面和业务逻辑更好的分离开来&#xff0c;页面与页面的跳转&#xff0c;逻辑关系&#xff0c;页面与后台不同的beans的对应和操作都是通过faces-config.xml文件来说明和配置。对程序员的要求不高&#xff0c;页面程序员可…

谈谈京东的服务框架JSF

谈谈京东的服务框架 最近由于在实习期间接触到了京东的自研服务框架JSF&#xff0c;简称“杰夫”&#xff0c;目前我写的一些新功能里面调用的下游接口就是杰夫提供的。现有有很多高效的服务框架&#xff0c;如阿里巴巴的Dubbo配合Apache的ZooKeeper&#xff0c;那么为什么京东…

JSF 转换与验证

在本文中&#xff0c;我们将介绍 JSF 转换和验证框架的概念&#xff0c;它比您所想的要容易使用得多&#xff0c;也灵活得多。 首先我们将介绍应用于 JSF 生命周期的转换和验证过程&#xff0c;然后展示一个简单的 JSF 应用程序中的默认转换和验证过程。接着将展示如何创建和插…

JSF----------基础知识初解

初次学习JSF,对其基础进行了一些学习与整理。 JSF(JavaServer Faces)它是一个基于服务器端组件的用户界面框架。 它用于开发Web应用程序。 它提供了一个定义良好的编程模型&#xff0c;由丰富的API和标签库组成。最新版本JSF 2使用Facelets作为其默认模板系统。 它是用Java编写…

JSF详解

1&#xff0e; 结构&#xff1a; a) 结构图&#xff1a; b) 说明&#xff1a;JSF以MVC模式为基础&#xff0c;与Struts不同&#xff0c;JSF的目标是希望以一个与Swing相类似的方式来开发网页&#xff0c;因此&#xff0c;从JSF的结构图当中&#xff0c;他的核心…

JSF框架整理(一)

一、框架简介 JavaServer Faces (JSF) 是一种用于构建Java Web 应用程序的标准框架&#xff0c;它提供了一种以组件为中心的用户界面&#xff08;UI&#xff09;构建方法&#xff0c;从而简化了Java服务器端应用程序的开发。 典型的JSF应用程序包含下列部分&#xff1a; 一组J…

JSF简介

JSF简介 一、 什么是 JSF &#xff1a; JavaServer Faces (JSF) 是一种用于构建 Web 应用程序的新标准 Java 框架。它提供了一种以组件为中心来开发 Java Web 用户界面的方法&#xff0c;从而简化了开发。 JavaServer Faces于2004年三月1.0版正式提出&#xff0c;清楚的将Web应…

JSF概述

1. JSF简洁 JSF是一种以组件为中心&#xff0c;遵循MVC设计模式的一种框架。 Web引用程序开发人员划分&#xff1a;网页设计人员应用程序设计人员UI组件设计人员 所有与应用程序都由一个前端控制器(FacesServlet)来处理 2. JSF声明周期 FacesServlet充当用户和JSF应用程序之间的…

ztree项目

思路&#xff1a; 创建一个登陆 登陆上去 就是树 每个是的根节点有他所要展示的内容 表 可以有无数个 主要说的是创建树的表 这个是树的一个表 id 是 节点 name 名字 pid 根节点 url 路径 树的页面 后台通过登陆转的页面 转页面 在前台打印出你想要的数据 前台页面 退出 /*…

ztree使用

官方文档地址 http://www.treejs.cn/v3/main.php#_zTreeInfo 各种参数 http://www.treejs.cn/v3/api.php 简单静态调用 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><link href"https://cdn…

zTree 简介

zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。 zTree 是开源免费的软件&#xff08;MIT 许可证&#xff09;。如果您对 zTree 感兴趣或者愿意资助 zTree 继续发展下去&#xff0c;可以进行捐助。 zTree v3.0 …

ztree 详解

官网:http://www.treejs.cn/v3/demo.php#_101 里面有例子和demo,齐全。 zTree是一个基于jQuery的树形列表生成控件。 切换语言,点击下载,里面有各种demo: 里面各种demo,比博客写的好。 <!DOCTYPE html> <html lang="en"><head><meta ch…

zTree的简单使用1.0

2018/10/10 北京朝阳.冠城大厦17楼 这里是引用 zTree 简介 zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。 zTree 是开源免费的软件&#xff08;MIT 许可证&#xff09;。如果您对 zTree 感兴趣或者愿意资助 zT…