(一三一)指向对象的指针

简介:

类对象也可以像基本类型那样,使用指针。

 

假如有Man类的对象a,可以使用指针指向它:

Man* c=&a;

这里表示指针c(类型为Man)指向对象a的地址(使用地址运算符&)。

 

也可以使用new

Man *a=new Man;

这是new一个动态内存地址,类型为Man,然后用a指向地址。

new的过程中,调用默认构造函数创建一个对象,被放在堆,而指针a指向的则是这个对象的地址。

 

 

 

关于析构函数的调用:

①当离开对象的作用域时,析构函数将被调用(例如代码块内声明的对象,在离开代码块时);

 

②静态对象,将在程序执行完毕时,调用静态对象的析构函数(main()函数结束);

 

new出来的对象,只在使用delete时,调用对象的析构函数。

 

 

当指针遇见对象时的几种情况:

假设类为Man,已声明对象Man one(注,已被初始化赋值);

Man *a;

一个普通的Man类型指针,尚未指向任何对象

 

Man *a=&one;

指针指向对象one的地址。

 

Man *a = new Man;

指针使用new申请动态内存创建新的Man对象,调用Man的 默认构造函数 

 

Man *a = new Man(one);

指针使用new申请动态内存创建新对象,并同时以对象one为参数进行初始化,调用Man的 复制构造函数 

 

Man *a = new Man[5];

指针指向动态数组,数组类型为Man,元素个数为5,并且每个都调用 默认构造函数 进行初始化。

 

Man *a = new Man("aabbcc");

指针new一个对象,这个对象用参数"aabbcc"进行初始化(假如有多个参数的话,可以用例如 (1,"aabbcc") 这样。调用 构造函数 。

 

 

 

当类指针调用类函数方法时:

已知,结构指针可以通过 -> 运算符来访问结构成员,也可以通过 (*结构对象).结构成员 来访问。类的指针也类似。

例如:

Man*a = new Man(1,"abc");

(*a).show(); //办法一

a->show(); //办法二

 

这两个的效果,是一样的。

 

 

 

 

new定位运算符:

已知,new定位运算符的效果是,使用某个指定地址的内存。

前提一:需要使用头文件<new>

前提二:这个地址不一定在堆中(甚至不一定需要同一个类型,比如char*使用int类型变量的地址)

前提三:程序员需要为这种使用负责(不会像new那样在堆中自动申请够足够的动态内存)

 

 

 

作用:

根据我搜索的一些内容显示,new定位运算符的作用有(有些意思可能是相互重复的):

①在特定地址调用构造函数

构造函数和析构函数的指针是无法获得的,那么怎么单纯的调用构造函数呢,这时候定位new就有用了

 

②硬件编程

如果知道了硬件设备的地址,想要将那个硬件设备与一个C++类直接关联,那么定位new就非常有效了

 

③实现基础库

基础库一般为了效率要先预分配内存,然后在预分配的内存上执行构造,几乎所有的C++容器10种,但我尚没搞懂是什么)都用到了定位new

 

④在先分配好的缓冲区中划分出一部分用作我们自己的内存规划。例如先new char[40],然后在这40char宽度的内存中,再自行使用。

 

⑤在特定位置创建对象。

 

注意:在同一个地址使用new,很可能会覆盖之前的数据。

 

大概就是以上作用。

 

配对:

new可以和delete,也需要和delete配对使用;

但是new定位运算符,不能和delete配对使用。

 

 

缓存区:

大概意思就是预先准备的一部分内存,

例如new char[80]就是80char宽度的内存,在未被delete前都不会被占用。

也可能是char a[80],这部分就在栈区了。

有点划地盘的感觉。

 

 

析构函数:

使用new定位运算符创建的对象,不应使用delete/delete[]

原因在于,new定位运算符一般被放在缓冲区之中(缓冲区有可能的是使用new xx[]申请的,但也有可能是在栈、或者静态存储区),这可能导致错误。

 

对于使用new定位运算符创建的对象,应该 显式的使用析构函数 。

这也是需要显式使用析构函数的少数几种情形之一。

 

因为是指针,所以要注意运算符。例如:one->~Man();

 

关于使用顺序,晚创建的对象(使用new定位运算符,特别是在同一地址),应该先调用析构函数(因为晚创建的对象,可能依赖于早创建的,不过不懂这句话),早创建的后调用析构函数。(有点类似栈的LIFO

 

之所以必须显式的调用析构函数,因为假如不显示调用析构函数的话,那么类对象则不会被销毁(就像使用new的对象一样,不受代码块结束所限制,只有使用了delete才会被调用析构函数而销毁)。

 

 

 

防止覆盖:

假如已经在缓存区内某个内存地址(one)使用new定位运算符创建了一个对象a,然后又想在这个缓存区内创建一个对象b,但不想让b覆盖对象a。那么就应该使用运算符sizeof()。

 

运算符sizeof的作用是返回对象的内存宽度,例如

cout << sizeof(*b) << endl;

cout << sizeof(Man<< endl;

这二者貌似差不多,不过书上用的是后者。

 

问题:这二者有区别么?

 

而在防止覆盖时,是这么使用的:

Man*b = new(one + sizeof(Man)) Man;

如果有需要,还可以在最后一个Man后面加上 ("qqq")之类的参数,调用 构造函数 进行初始化。

 

 

如代码:

#include<iostream>
#include<new>	//第一次我忘记加了,但感觉加不加似乎结果都一样

class Player
{
	char* name;	//名字(指针)
	int id;	//编号(整型)
	static int num;	//总数(计数器)
public:
	Player();	//默认构造函数
	~Player();	//析构函数
	void show();	//显示
};

Player::Player()
{
	name = new char[2];	//使用new[],那么对应应该使用delete[],且其他构造函数应该保持一致
	*name = 'a' + num;	//空字符占位
	name[1] = '\0';
	id = ++num;
	std::cout << "地址:" << this << ",姓名:" << name << ",ID:" << id << " 被创建,当前对象总数:" << num << std::endl;	//通报地址及其他数据
}
Player::~Player()	//注意波浪线位置
{
	std::cout << "地址:" << this << ",姓名:" << name << ",ID:" << id << " 已经删除,当前对象总数:" << --num << std::endl;	//通报地址及其他数据
	delete[]name;
}
void Player::show()
{
	std::cout << "地址:" << this << ",姓名:" << name << ",ID:" << id << ",当前对象总数:" << num << std::endl;
}
int Player::num = 0;
int main()
{
	using namespace std;
	Player *B, *C, *D;
	char *M;	//代码块外定义指针

	{
		cout << "*************这里代码块开始*************" << endl;
		Player a;
		Player *b = new Player;
		char *m = new char[100];	//创建缓存区,宽度为100个char
		cout << "缓存区m的地址是:" << (void*)m << ",以下2个使用new定位运算符" << endl;	//这里之所以用(void*m),是因为m指向字符串,默认显示字符串,所以需要使用强制类型转换来显示地址
		Player*c = new(m)Player;	//使用new定位运算符
		Player *d = new(m + sizeof(Player))Player;	//使用new定位运算符在缓存区,保证新对象不覆盖老对象
		B = b, C = c, D = d, M = m;	//让代码块外定义的指针分别指向代码块内定义的指针,防止代码块结束后,声明的指针消失
	}
	cout << "*************这行之前,代码块结束*************" << endl;
	delete B;	//之所以不能deleteb,是因为b是在代码块内定义的

	D->~Player();
	(*C).~Player();	//显式调用对象指针的两种方式,先用的后调用
	delete[]M;	//最后再删除缓存区
	system("pause");
	return 0;
}

显示:

*************这里代码块开始*************
地址:003EFE8C,姓名:a,ID:1 被创建,当前对象总数:1
地址:00470DC0,姓名:b,ID:2 被创建,当前对象总数:2
缓存区m的地址是:00470AE8,以下2个使用new定位运算符
地址:00470AE8,姓名:c,ID:3 被创建,当前对象总数:3
地址:00470AF0,姓名:d,ID:4 被创建,当前对象总数:4
地址:003EFE8C,姓名:a,ID:1 已经删除,当前对象总数:3
*************这行之前,代码块结束*************
地址:00470DC0,姓名:b,ID:2 已经删除,当前对象总数:2
地址:00470AF0,姓名:d,ID:4 已经删除,当前对象总数:1
地址:00470AE8,姓名:c,ID:3 已经删除,当前对象总数:0
请按任意键继续. . .


目录
相关文章
|
5月前
|
存储 编译器 程序员
【C++】类与对象(一)类的定义 访问限定符 类的实例化 this指针
【C++】类与对象(一)类的定义 访问限定符 类的实例化 this指针
|
5月前
|
编译器 C语言 C++
类和对象(1):类,对象,this指针
类和对象(1):类,对象,this指针
|
22天前
|
存储 编译器 C语言
【c++】类和对象(二)this指针
朋友们大家好,本节内容来到类和对象第二篇,本篇文章会带领大家了解this指针
【c++】类和对象(二)this指针
|
1月前
|
C++
c++类和对象一对象特楼一this指针的用途讲解
c++类和对象一对象特楼一this指针的用途讲解
8 1
|
2月前
|
存储 C++
C++类与对象【对象模型和this指针】
C++类与对象【对象模型和this指针】
|
2月前
|
编译器 C语言 C++
C++类和对象的细节原理:this指针、构造函数和析构函数、深浅拷贝、运算符重载、初始化列表、类的各种成员和方法
C++类和对象的细节原理:this指针、构造函数和析构函数、深浅拷贝、运算符重载、初始化列表、类的各种成员和方法
36 0
|
3月前
|
存储 Java 编译器
C++——类和对象(了解面向过程和面向对象、初步认识类和对象、类大小的计算、this指针)
C++——类和对象(了解面向过程和面向对象、初步认识类和对象、类大小的计算、this指针)
|
3月前
|
存储 编译器 C语言
C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针
C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针
43 0
|
4月前
|
Java
又是把Java基础知识学废的一天,new 一个对象数组,操作时报空指针异常
又是把Java基础知识学废的一天,new 一个对象数组,操作时报空指针异常
36 0
|
4月前
|
Java Spring
SpringMVC控制层private方法中出现注入的service对象空指针异常
一、现象 SpringMVC中controller里的private接口中注入的service层的bean为null,而同一个controller中访问修饰符为public和protected的方法不会出现这样的问题。 controller中的方法被AOP进行了代理,普通Controller如果没有AOP,private方法中bean也是正常的。

热门文章

最新文章