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. # electron-vue 尝试做个网易云音乐

    当跑起来electron第一刻 我发现这个浏览器头是不是有点丑 是不是可以隐藏起来呢,答案当然是可以的 src/main/index.js mainWindow = new BrowserWindow ...

  2. JavaScript 题目(作用域)

    var length = 10 function fn(){ alert(this.length) } var obj = { length: 5, method: function(fn) { fn ...

  3. 吴恩达深度学习第2课第3周编程作业 的坑(Tensorflow+Tutorial)

    可能因为Andrew Ng用的是python3,而我是python2.7的缘故,我发现了坑.如下: 在辅助文件tf_utils.py中的random_mini_batches(X, Y, mini_b ...

  4. 微信小程序--试水

    应公司需求,接手小程序,在此之前我是一点也没有接触过,对此,拿过小程序文档和官方案例就一顿恶补,在此期间也看过一些小程序建立模型的视频,终于对小程序知晓一二,拿过项目开始研究.好了废话不多说,总结一下 ...

  5. ActiveMQ消息传递的两种方式

    1.什么是ActiveMQ? ActiveMQ是apache提供的开源的,实现消息传递的一个中间插件,可以和spring整合,是目前最流行的开源消息总线,ActiveMQ是一个完全支持JMS1.1和J ...

  6. python基础学习(一)

    python简介 python的创始人为吉多·范罗苏姆(Guido van Rossum),诞生时间1989年圣诞 一.变量的命令规则 1.变量只能由大小写字母.数字和下划线三部分组成,并且不能以数字 ...

  7. delphi 面向对象实用技能教学一(递归)

    本例使用类与TList相结合,用简洁的方法,实现了一个 HTML 解析与格式化功能.所用到的知识点如下:1.类的提前申明2.TList用法3.String的指针操作4.单例设计5.递归用法 编程是综合 ...

  8. MySQL注释中的sql也可能执行

    MySql支持三种注释形式:# 和–属于单行注释,注释范围为该行的结尾:/* */注释属于多行注释,此外该种注释还可以实现行内注释.具体的使用情况如下图中所示(四种使用情形): 除此之外,/* */这 ...

  9. Android 内置群组,联系人

    这样一个需求,手机第一次启动的时候,需要内置一个群组,并且里面有给定的联系人信息, 本来打算写双进程守护的,结果昨天接到一个这样的任务,就先把它做了,发现里面有些操作数据库的东西还是值得看一下. 首先 ...

  10. ROS探索总结(十九)——如何配置机器人的导航功能

    1.概述 ROS的二维导航功能包,简单来说,就是根据输入的里程计等传感器的信息流和机器人的全局位置,通过导航算法,计算得出安全可靠的机器人速度控制指令.但是,如何在特定的机器人上实现导航功能包的功能, ...