类对象也可以像基本类型那样,使用指针。
假如有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 请按任意键继续. . .