C++类的指针成员与其他成员有所不同,指针成员指向一个内存地址,该地址的内存需要我没管理。

我现在分析一下为什么要管理指针成员。

有如下Student类,Student.h如下:

  1. class Student
  2. {
  3. public:
  4. Student(int *books);
  5. virtual ~Student();
  6. int *books;
  7. };

Student.cpp如下:

  1. #include "Student.h"
  2. Student::Student(int *books)
  3. {
  4. this->books=books;
  5. }
  6. Student::~Student()
  7. {
  8. }

在主函数中我如下写:

  1. #include <iostream>
  2. #include "Student.h"
  3. using namespace std;
  4. int main()
  5. {
  6. int *b=new int(34);
  7. Student s(b);
  8. delete b;
  9. cout<<(*(s.books))<<endl;
  10. return 0;
  11. }

当我释放掉了b所指的空间,b的地址就不能用了,但是在s中任然可以访问,所以就出现了不可预知的错误

最后输出的结果如下:

这个指针所指的空间需要我们的Student类来管理,因为我们不知道什么时候该释放掉b的空间,如果手动释放掉b的空间,然后再使用Student对象引用该空间的值,就会出现上面的错误。更复杂的情况是使用一个Student对象初始化另外一个Student对象的时候和赋值的时候。

管理指针成员有两种办法

1.定义智能指针类

2.定义值类型

下面分别介绍

1.智能指针

智能指针:一个行为类似指针但也提供其他功能的类。智能指针的一个通用形式接受指向动态分配对象的指针并负责删除掉该对象,用户分配对象,但由智能指针类删除掉它。智能指针类需要实现复制控制成员来管理指向共享对象的指针。只有在撤销了指向共享对象的最后一个智能指针后,才能删除该共享对象。使用计数是实现智能指针类最常用的方式。

计数类的实现

我先设计一个数据类,目的是察看智能指针的效果。数据类就一个数据,int类型,我主要在析构函数中输出删除了该对象。Flag.h头文件:

  1. class Flag {
  2. public:
  3. Flag(int v);
  4. virtual ~Flag();
  5. int i;
  6. };

Flag.cpp源文件:

  1. #include "Flag.h"
  2. #include <iostream>
  3. Flag::Flag(int v) {
  4. // TODO Auto-generated constructor stub
  5. i=v;
  6. }
  7. Flag::~Flag() {
  8. std::cout<<"delete the value"<<std::endl;
  9. }

下面实现记述类:U_Ptr.h文件:

  1. #include "Flag.h"
  2. class U_Ptr {
  3. public:
  4. friend class Student;
  5. U_Ptr(Flag *p);
  6. virtual ~U_Ptr();
  7. private:
  8. Flag *books;
  9. unsigned int use;
  10. };

U_Ptr.cpp源文件:

  1. #include "UPtr.h"
  2. U_Ptr::U_Ptr(Flag *p) {
  3. this->books=p;
  4. this->use=1;
  5. }
  6. U_Ptr::~U_Ptr() {
  7. delete books;
  8. }

这个类设置Student类为它的友元,以便在Student类中访问该类的私有成员。该类接收一个Flag的指针,该内存在其他地方分配,由U_Ptr类来管理,当该类被释放的时候回收Flag指针所指内存。

下面我完成智能指针类Student,该类决定在什么样的情况下删除Flag指针所分配的内存。

Student.h头文件:

  1. #include "UPtr.h"
  2. #include "Flag.h"
  3. class Student {
  4. public:
  5. Student(Flag *p);
  6. Student(const Student &s);
  7. Student& operator=(const Student &s);
  8. int getValue();
  9. void setValue(int number);
  10. virtual ~Student();
  11. private:
  12. U_Ptr *ptr;
  13. };

该类包含了一个U_Ptr对象的指针,在这个类中,要对复制与赋值进行控制。

Student.cpp源文件:

  1. #include "Student.h"
  2. Student::Student(Flag *p) {
  3. this->ptr=new U_Ptr(p);
  4. }
  5. Student::Student(const Student &s)
  6. {
  7. this->ptr=s.ptr;
  8. ++ptr->use;
  9. }
  10. Student& Student::operator=(const Student &s)
  11. {
  12. ++s.ptr->use;
  13. if(--ptr->use==0)
  14. {
  15. delete ptr;
  16. }
  17. ptr=s.ptr;
  18. return *this;
  19. }
  20. int Student::getValue()
  21. {
  22. return (ptr->books)->i;
  23. }
  24. void Student::setValue(int number)
  25. {
  26. (ptr->books)->i=number;
  27. }
  28. Student::~Student() {
  29. if(--ptr->use==0)
  30. {
  31. delete ptr;
  32. }
  33. }

在构造函数中,为ptr指针分配内存,但是这个内存在什么时候释放掉要看该ptr的use的计数,当use为0的时候,说明没有其他的Student类共享该ptr所指内存,这时就释放内存。

在赋值的时候,先将右值的计数加一,本对象的计数应该减一,并且判断这时是否应该释放该对象,然后将右值的ptr赋值给本对象。这样如果是相同的对象赋值的话,有安全的保障。如果先减一,刚好计数为1,会释放内存,然后将计数加一。

在主函数中执行下面的代码会得到我希望的结果:

  1. #include <iostream>
  2. using namespace std;
  3. #include "Flag.h"
  4. #include "Student.h"
  5. int main() {
  6. Flag *f=new Flag(43);
  7. Student s1(f);
  8. Student s2(s1);
  9. return 0;
  10. }

打印出   delete the value.

2.定义值类型

这种方法比较简单,看代码了:

Human.h类就是这样的类:

Human.h

  1. class Human {
  2. public:
  3. Human(int value);
  4. Human(const Human &value);
  5. Human& operator=(const Human &value);
  6. virtual ~Human();
  7. private:
  8. int *ip;
  9. };

Human.cpp

  1. #include "Human.h"
  2. Human::Human(int value) {
  3. // TODO Auto-generated constructor stub
  4. ip=new int(value);
  5. }
  6. Human::Human(const Human &value) {
  7. // TODO Auto-generated constructor stub
  8. ip=new int(*(value.ip));
  9. }
  10. Human& Human::operator=(const Human& value)
  11. {
  12. *ip=*(value.ip);
  13. return *this;
  14. }
  15. Human::~Human() {
  16. // TODO Auto-generated destructor stub
  17. delete ip;
  18. }

在构造函数中为指针分配内存,在析构函数中释放内存,在赋值操作中改变指针所指内容的值而不是指针的值。

C++ Primer 有感(管理类的指针成员)的更多相关文章

  1. C++ Primer 有感(类)

    1.在类内部,声明成员函数时必需 的,而定义成员函数则是可选的.在类内部定义的函数默认为inline. 2.const成员函数不能改变其所操作的对象的数据成员.const必须同时出现在声明和定义中,若 ...

  2. C++ Primer 学习笔记_57_类和数据抽象 --管理指针成员

    复印控制 --管理指针成员 引言: 包括指针的类须要特别注意复制控制.原因是复制指针时.一个带指针成员的指针类 class HasPtr { public: HasPtr(int *p,int i): ...

  3. 【c++】类管理指针成员

    c++编程提倡使用标准库,一个原因是标准库大胆减少对指针的使用.但是许多程序是离不开指针的.包含指针的类需要特别注意复制控制,原因是复制指针时只复制指针中的地址,而不复制指针所指向的对象.这样当把一个 ...

  4. C++ 带有指针成员的类处理方式

    在一个类中,如果类没有指针成员,一切方便,因为默认合成的析构函数会自动处理所有的内存.但是如果一个类带了指针成员,那么需要我们自己来写一个析构函数来管理内存.在<<c++ primer&g ...

  5. C++智能指针管理类

    1.程序员明确的进行内存释放 对于c++程序员,最头脑的莫过于对动态分配的内存进行管理了.c++在堆上分配的内存,需要程序员负责对分配的内存进行释放.但有时内存的释放看起来并不件很轻松的事,如下程序 ...

  6. 类1(this指针/const成员函数/类作用域/外部成员函数/返回this对象的函数)

    假设我们要设计一个包含以下操作的 Sales_data 类: 1.一个 isbn 成员函数,用于返回对象的 book_no 成员变量 2.一个 combine 成员函数,用于将一个 Sales_dat ...

  7. C++管理指针成员

    1.C++中一般採用以下三种方法之中的一个管理指针成员: (1)指针成员採取常规行为. 这种类具有指针的全部缺陷:具有指针成员且使用默认复制构造函数和赋值操作符,无法避免悬垂指针(两个对象的指针成员指 ...

  8. YTU 2636: B3 指向基类的指针访问派生类的成员函数

    2636: B3 指向基类的指针访问派生类的成员函数 时间限制: 1 Sec  内存限制: 128 MB 提交: 433  解决: 141 题目描述 领导类(Leader)和工程师类(Engineer ...

  9. 15.含有指针成员的类的拷贝[ClassCopyConstructorWithPointerMember]

    [题目] 下面是一个数组类的声明与实现.请分析这个类有什么问题,并针对存在的问题提出几种解决方案.  C++ Code  123456789101112131415161718192021222324 ...

随机推荐

  1. 48. Rotate Image(中等)

    You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise). ...

  2. 下拉框多级联动辅助js,优化您的下拉框

    function IniteSelect(options) { $("body").IniteSelect(options) } (function ($) { $.fn.Init ...

  3. Node.js TTY

    稳定性: 2 - 不稳定 tty 模块包含 tty.ReadStream 和 tty.WriteStream 类.多数情况下,你不必直接使用这个模块. 当 node 检测到自己正运行于 TTY 上下文 ...

  4. 基于Python预测股价

    ▌实现预测的Stocker工具 Stocker是一款用于探索股票情况的Python工具.一旦我们安装了所需的库(查看文档),我们可以在脚本的同一文件夹中启动一个Jupyter Notebook,并导入 ...

  5. jQuery 效果 – 停止动画

    jQuery stop() 方法用于在动画或效果完成前对它们进行停止. 点击这里,向上/向下滑动面板 实例 jQuery stop() 滑动 演示 jQuery stop() 方法. jQuery s ...

  6. Docker外部访问容器

    容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射. 当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放 ...

  7. Azure AI 服务之语音识别

    笔者在前文<Azure AI 服务之文本翻译>中简单介绍了 Azure 认知服务中的文本翻译 API,通过这些简单的 REST API 调用就可以轻松地进行机器翻译.如果能在程序中简单的集 ...

  8. Python安装与使用的常见问题

    1. Python安装问题 到Python官网下载Python最新版本 Windows x86-64 executable installer (64为操作系统选择这个) Windows x86 ex ...

  9. Android的AIDL机制

    Android 接口定义语言 (AIDL) AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似. 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的 ...

  10. PHP 验证码 浅析

    拓展 背景图 imagecreatetruecolor imagecolorallocate imagepng imagedestoryimage 简易数字验证码 imagecolorallocate ...