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

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

 

假如有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],然后在这40个char宽度的内存中,再自行使用。

 

⑤在特定位置创建对象。

 

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

 

大概就是以上作用。

 

配对:

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

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

 

 

缓存区:

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

例如new char[80]就是80个char宽度的内存,在未被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
请按任意键继续. . .
时间: 2025-01-19 03:21:28

(一三一)指向对象的指针的相关文章

解析C++中指向对象的指针使用_C 语言

C++指向对象的常指针将指针变量声明为const型,这样指针值始终保持为其初值,不能改变. 如: Time t1(10,12,15),t2; //定义对象 Time * const ptr1; //const位置在指针变量名前面,规定ptr1的值是常值 ptr1=&t1; //ptr1指向对象t1,此后不能再改变指向 ptr1=&t2; //错误,ptr1不能改变指向 定义指向对象的常指针的一般形式为: 类名 * const 指针变量名; 也可以在定义指针变量时使之初始化,如将上面第2,

C++中指向对象的常指针与指向常对象的指针详解_C 语言

指向对象的常指针 将指向对象的指针变量声明为const型,并使之初始化,这样指针值始终保持为其初始值,不能改变. 复制代码 代码如下: Time t1(10,12,15),t2;Time * const ptr1=&t1;ptr1=&t2; 定义指向对象的常指针的一般形式为 类名    *    const    指针变量=对象地址; 注意应该在定义指针变量时使之初始化 指向对象的常指针变量的值不能被改变,即始终指向同一个对象,但可以改变其所指向对象中的数据成员(非const型)的值. 往

C++指向函数的指针实例解析_C 语言

通常来说C++函数指针是指指向函数的指针,而非指向对象的指针.就像其他指针一样,函数指针也指向某个特定的类型.函数类型由其返回类型以及形参表确定,而与函数名无关. 定义: char (*fP)(char,int); 赋值: char function(char i,int j) { } fp=function; 调用 (*fp)(10,100); type char (*FUN)(char,int);//类型定义 FUN fp ;//定义fp为指向函数的指针 volatile的用法和作用: co

将指向结构体对象的指针作为函数参数,调用p-&amp;amp;gt;时调试报错

问题描述 将指向结构体对象的指针作为函数参数,调用p->时调试报错 void deleteelement(linearlist *list, int power) { linearlist *p = list; while (p->power != power && p->next != NULL) //调试时显示错误在这一行 { p = p->next; } if (p->power == power) { linearlist *dele = p; p =

【C/C++学院】0820-Nullptr/const对象/类指针引用以及mallocfree与newde/类重载运算符/QT加法重载/类的重载赋值运算/自增在前在后差别/赋值重载深浅拷贝/重载下标

Nullptr #include<iostream> void go(int num) { std::cout << "gonum" << std::endl; } void go(void *p) { std::cout << "gop" << std::endl; } void main() { //void *p = nullptr; void *p = NULL;//C++是强类型,严格的类型检查

《C和C++代码精粹》——2.12 指向函数的指针

2.12 指向函数的指针 C和C++代码精粹 一个指针可以指向函数也可以指向存储的对象.下面的语句声明fp是一个指向返回值为整型(int)的函数的指针: int(*fp)( ); *ftp的圆括号是必需的,没有它的语句 int *fp( ); 将fp声明为一个返回指向整型(int)指针的函数.这就是将星号与类型声明紧密相连的方式成为逐渐受人们欢迎的方式的原因之一. int fp(); //方式说明fp()返回一个指向整型的指针(int ) 当然,这种方式建议你通常应该每条语句只声明一个实体,否则

C++编程指向成员的指针以及this指针的基本使用指南_C 语言

指向成员的指针指向成员的指针的声明是指针声明的特例.使用以下序列来声明它们: [storage-class-specifiers] [cv-qualifiers] type-specifiers [ms-modifier] qualified-name ::* [cv-qualifiers] identifier [= & qualified-name :: member-name]; 声明说明符: 可选存储类说明符. 可选 const 和/或 volatile 说明符. 类型说明符:类型的名称

C语言const指针和指向const的指针的理解

1.const 的理解     const 是C语言(C++)的一个关键字,需要注意的是,const 关键字是把变量变为一个只读的变量,绝对不是将这个变量变为常量.也就是说经过const 修饰的变量成为只读的变量之后,那么这个变量就只能作为右值(只能赋值给别人),绝对不能成为左值(不能接收别人的赋值).       经过const 修饰的变量,在定义的时候,就要进行初始化. const int a = 10;  // 正确 const int a;  // 错误       const 关键字最

函数指针 c语言 指针-C语言指向函数的指针的调用

问题描述 C语言指向函数的指针的调用 int *d_bubblesort(int a[]int n)//数组长度为n的冒泡降序排序{int ij;int temp;for(i=0;i {for(j=n-1;j>i;j--){if(a[j]>a[j-1]){temp=a[j-1];a[j-1]=a[j];a[j]=temp;} }}return(a);} void main(){int i;int *p;int a[10]={65412398710};int (*fun)(intint);fun