指针:指针变量,可以用用来引用数组,对象,任何变量的地址。指针中保存的是地址,用*(解引运算符)来访问内存中的数据

指针的使用:

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串 using namespace std;int main(int argc, char *argv[])
{int count = 5;int* p;p = &count;cout << "The vauel of count is " << *p << endl;cout << "The address of count is " << p << endl; return 0;
}

与局部变量一样,如果不为局部指针赋值,其内容是任意的,可以将一个指针值赋值为0,这是一个特殊的指针值,表示指针未指向任何变量。因此,应该始终对指针保持初始化。以避免错误。

2.用typedef定义同义类型

typedef  int*  intPointer

typedef并不是创造新的数据类型,而是给已有的数据类型起一个别名

3.常量指针

常量指针指向一个不变的内存位置,但是内存中的数据是可以改变的

double radius = 5;

double* const p = &radius;   //常量指针

const double *p = &radius;     // 指针指向的数据(radius)是常量

const double* const p = &radius;    // 指向常量的常量指针

4. 数组和指针

在c++中,数组名实际上是指向数组第一个元素的常量指针,数组与指针关系密切。

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串 using namespace std;int main(int argc, char *argv[])
{int list[6] = {1,2,3,4,5,6};int* p = list;for(int i=0; i<6; i++){cout << "Adress: " << (p+i) << " value: " << *(p+i) << " value: " << p[i] << endl;}return 0;
}

5. 函数调用时传递指针参数

函数的参数可以是指针,指针参数可以通过值传递或者引用传递的方式进行传递

例如下面的swap()函数

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串 using namespace std;void swap1(int a, int b)
{int tmp = a;a = b;b = tmp;
} void swap2(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}void swap3(int* pa, int* pb)
{int tmp = *pa;*pa = *pb;*pb = tmp;
}void swap4(int* &a, int* &b)
{int tmp = *a;*a = *b;*b = tmp;
}int main(int argc, char *argv[])
{int a = 1;int b = 2;swap1(a,b);cout << "After swap a is " << a << " and b is " << b << endl;a = 1;b = 2;swap2(a, b);cout << "After swap a is " << a << " and b is " << b << endl;a = 1;b = 2;int* p1 = &a;int* p2 = &b;swap3(p1, p2);cout << "After swap a is " << a << " and b is " << b << endl;a = 1;b = 2;p1 = &a;p2 = &b;swap4(p1, p2);cout << "After swap a is " << a << " and b is " << b << endl;return 0;
}

运行结果:

swap1()未实现交换

函数传引用, 形参相当于实参的一个别名

6. 从函数中返回指针

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串 using namespace std;int* reverse(int*, int);
void printArray(int*, int);int main(int argc, char *argv[])
{int size = 5;  // 数组名不能动态定义 int a[5] = {1,2,3,4,5};int* p = reverse(a, size);printArray(p, size);return 0;
}int* reverse(int* list, int SIZE)
{for(int i=0, j=SIZE-1; i<j; i++, j--){int tmp = list[i];list[i] = list[j];list[j] = tmp; }return list;
}void printArray(int* list, int SIZE)
{for(int i=0; i<SIZE; i++){cout << list[i];}cout << endl;
}

函数reverse()返回一个指针p。传入的第一个参数是实参(数组)的地址,实际上返回的也是同一个地址。

7,有用的数组函数

min_element

max_element

sort

random_shuffle

find:find函查找相应的元素,如果找到,返回的是数组元素对应的指针,如果没有找到,返回数组最后一个元素的后一个位置的指针,这就是为什么这些函数的参数为(list, list +size), 多出的一个指针应该就是分配给未找到元素时的这种情况。

这些对数组进行操作的函数都在头文件algotithm中,这些函数的参数都是指针,返回的都是相应元素的指针。

例子:

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串
#include <algorithm> using namespace std;void printArray(int*, int);
int main(int argc, char *argv[])
{int size = 6;int list[] = {4, 2, 3, 6, 5, 1};printArray(list, size);int* min = min_element(list, list+6);  // 参数是指针 int* max = max_element(list, list+6);  // 参数cout << "The minum is of list is " << *min << " at index " << (min - list) << endl;  //注意这里的指针相减  cout << "The maxum is of list is " << *max << " at index " << (max - list) << endl;  //注意这里的指针相减  random_shuffle(list, list+6);printArray(list, size);sort(list, list+6);printArray(list, 6);// findint key = 4;  // 要查找的元素int* p = find(list, list+6, key);  if(p!=(list+6))  // 没找到要查找的元素 {cout << "The value " << *p << "is found at index " << (p-list) << endl;}else{cout << "The value " << *p << "is not found" << endl;}  return 0;
}void printArray(int* list, int SIZE)
{for(int i=0; i<SIZE; i++){cout << setw(3) << list[i];}cout << endl;
}

p != list+6, 证明找到了相应的元素

8,动态持久内存分配:
new操作符可以在运行时为基本数据类型,数组和对象分配持久的内存空间。

int *p = new int(4);   //为一个整形变量动态分配内存空间, 将地址赋值给指针pcin >> size;int *list = new int[size];  //动态数组, 数组大小在程序运行时输入 

动态数组, 数组大小在程序运行时输入,而创建一个普通数组时,数组的大小在程序编译时就已经定下来了
使用new操作符分配的内存是持久存在的,直到它被显式的释放或者程序退出。

delete p;      //数

delete []p;   // 数组

例子:重新实现数组reverse()

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串
#include <algorithm> using namespace std;void printArray(int*, int);
int* reverse(int*, int);int main(int argc, char *argv[])
{int SIZE = 10;int* p = new int[SIZE];for(int i=0; i<SIZE; i++){p[i] = i* 3 + 3;}printArray(p, SIZE);int* p1 = reverse(p, SIZE);printArray(p, SIZE);printArray(p1, SIZE);return 0;
}int* reverse(int* list, int size)
{int* result = new int[size];for(int i=0,j=size-1; i<size; i++,j--){result[i] = list[j];}return result;
}void printArray(int* list, int SIZE)
{for(int i=0; i<SIZE; i++){cout << setw(3) << list[i];}cout << endl;
}

C++中,局部变量的内存空间是在栈中分配的,而由new操作符分配的内存空间则出自称为“自由存储区域”或者“堆”的存储区域。

分配的空间一直是可用的,直至释放。

内存泄漏:

如下的代码:

 int* p = new int;*p = 45;p = new int;

第一句: 将一个动态内存赋值给指针p;

第二局:给变量赋值45;

第三局: 重新给p赋值一个新的地址值

这样会导致保存p指向的内存空间改变,存储45的内存将无法再访问,而这段内存也无法释放,这就是内存泄漏。

9. 创建及访问动态对象:
调用对象的构造函数可以动态的创建对象,new className(arguments),对象的地址会被赋值给相应的指针,调用对象时任然用解引运算符*

例如:

int main(int argc, char *argv[])
{string* p1 = new string();   // 无参构造函数 string* p2 = new string("hello world");   // 有参数的构造函数 // 动态对象的调用(*p2).substr(0,3);(*p1).append(" C++");cout << (*p1) << endl;cout << (*p2) << endl; delete p1;delete p2;return 0;
}

end

this指针:指向被调用的对象本身。

this指针是为了解决这里碰到的问题:https://blog.csdn.net/zj1131190425/article/details/82888131

即类中成员方法的参数名如果与数据域的变量名相同,就会出现覆盖的问题

例如之前的circle类: 成员函数的参数名不能命名为radius,因为radius是数据域

利用this指针就可以实现

circle类实现如下:

Circle::Circle(double radius)
{this->radius = radius;number_of_obj++;    // ++
}
void Circle::setRadius(double radius)
{this->radius = radius;
}

9. 析构函数

每一个类都有一个析构函数,当对象销毁时将自动调用该函数,析构函数与构造函数是相对的概念,如果类中没有定义析构函数,编译器将自动定义一个缺省的析构函数,析构函数与构造函数一样,与类名相同,但前面加上~符号

作用: 回忆之前的Circle类, 其中数据域有一个static类型的变量。用来记录创建的对象的个数,每当创建一个对象,numberOfObject加一。 如果采用new运算符创建动态对象, 在对象调用完毕时,会使用delete删除指向对象的指针,这时对象会被销毁,就需要执行一个析构函数,实现numberOfObject减一。

Circle::~Circle()   // 析构函数,在对象销毁时调用
{number_of_obj--;
}

Course类的实现:

实现一个课程类,用于选课学生数据的保存:

数据域包括:

课程名 courseName

选课容量  capacity

选课人数    student_number

学生名单    students

类成员方法:

add_student()      // 添加学生

drop_student()     // 删除学生

get_student_list()

get_courseName()

get_student_number()

同时会用到构造函数和析构函数:

code:

course.h

// course类的定义
#ifndef COURSE_H
#define COURSE_H
#include <string>
using namespace std;
class Course
{private:   // 数据域int courseCapacity;         // 课程容量int student_number;   // 记录选课学生人数string courseName;    // 课程名称string* students;     // 存储学生的数组, string【capacity】public:    // 成员方法 Course(const string& courseName, int courseCapacity);      // constructor~Course();       // 析构函数string getCourseName() const;  // 只读函数void addStudent(const string& name);void dropStudent(const string& name);string* getStudents() const;    // 返回学生的名单int getNumberOfStudent() const;
};
#endif 

course.cpp    course类的实现

// 类的实现
#include "E:\back_up\code\c_plus_code\chapter8\external_file\course.h"
#include <string>
#include <iostream>
using namespace std;
Course::Course(const string& courseName, int courseCapacity)
{this->courseName = courseName;this->courseCapacity = courseCapacity;students = new string[courseCapacity]; student_number = 0;
} Course::~Course()
{delete []students;   // 删除students指针
}string Course::getCourseName() const
{return courseName;
}void Course::addStudent(const string& name)
{if(student_number >= courseCapacity){cout << "you cat not add student because the course is full." << endl; }    else{students[student_number++] = name;}
}void Course::dropStudent(const string& name)
{bool found_flag = false;int index = 0;for(int i=0; i<student_number-1; i++){if(name==students[i]){found_flag = true;index = i;break;}} if(found_flag){for(int i=index; i<student_number-2; i++){students[i] = students[i+1];}  student_number--;cout << "Student " << name << " is successfully deleted from " << courseName << endl;}else    // 如果学生不在名单中 {cout << "Student " << name << " is not in the course " << courseName << endl;}
}string* Course::getStudents() const
{return students;
}int Course::getNumberOfStudent()   const
{return student_number;
}

main.cpp

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串
#include <algorithm>
#include "E:\back_up\code\c_plus_code\chapter8\external_file\course.h"using namespace std;void printStudentList(string*, int);int main(int argc, char *argv[])
{Course course1("Mathg", 3);//Course course2("Data Analyse", 4);cout << "course1's name is " << course1.getCourseName() << " and now it has " << course1.getNumberOfStudent() << " students" << endl;course1.addStudent("Tonny");course1.addStudent("Andrea");cout << "course1's name is " << course1.getCourseName() << " and now it has " << course1.getNumberOfStudent() << " students" << endl;cout << "The student list: " << endl;printStudentList(course1.getStudents(), course1.getNumberOfStudent());course1.addStudent("wang hau");cout << "course1's name is " << course1.getCourseName() << " and now it has " << course1.getNumberOfStudent() << " students" << endl;cout << "The student list: " << endl;printStudentList(course1.getStudents(), course1.getNumberOfStudent());course1.addStudent("huhuhu");   // 课程已满 course1.dropStudent("zhangjun");   // 删除不存在的学生 course1.dropStudent("Andrea");cout << "course1's name is " << course1.getCourseName() << " and now it has " << course1.getNumberOfStudent() << " students" << endl;cout << "The student list: " << endl;printStudentList(course1.getStudents(), course1.getNumberOfStudent());//Course course2("DataScience", 50); return 0;
}void printStudentList(string* student_list, int num)
{for(int i=0; i<num; i++){cout << student_list[i] << endl;}cout << endl;
}

运行结果:

拷贝构造函数:每一个类都有一个都有一个拷贝构造函数,用于拷贝对象。拷贝构造函数可以用来创建一个对象,并用另一个对象的数据初始化新建对象。每个类都会有一个缺省的拷贝构造函数

Circle(const Circle&)

拷贝构造函数和按成员赋值运算符(=)是对对象赋值采用的一种浅拷贝。

自定义拷贝构造函数,实现深拷贝:

Course类中:

代码:

course.h

// course类的定义
#ifndef COURSE_H
#define COURSE_H
#include <string>
using namespace std;
class Course
{private:   // 数据域int courseCapacity;         // 课程容量int student_number;   // 记录选课学生人数string courseName;    // 课程名称string* students;     // 存储学生的数组, string【capacity】public:    // 成员方法 Course(const string& courseName, int courseCapacity);      // constructor~Course();       // 析构函数Course(const Course& course);  // 拷贝构造函数 string getCourseName() const;  // 只读函数void addStudent(const string& name);void dropStudent(const string& name);string* getStudents() const;    // 返回学生的名单int getNumberOfStudent() const;
};
#endif

course.cpp:

// 类的实现
#include "E:\back_up\code\c_plus_code\chapter8\external_file\course.h"
#include <string>
#include <iostream>
using namespace std;
Course::Course(const string& courseName, int courseCapacity)
{this->courseName = courseName;this->courseCapacity = courseCapacity;students = new string[courseCapacity]; student_number = 0;
} Course::~Course()
{delete []students;   // 删除students指针
}Course::Course(const Course& course)   // 拷贝构造函数,自定义实现,深拷贝
{courseName = course.courseName;courseCapacity = course.courseCapacity;    // deep copystudent_number = course.student_number;students = new string[courseCapacity];     // deepcopy实现两个copy的对象数据域指针students应该相互独立,//如果用浅拷贝(默认的拷贝构造函数或者“=”浅拷贝。//两个指针是指向相同的地址,这样在调用析构函数是,//同一个指针会delete两次,导致程序报错for(int i=0; i<student_number; i++){students[i] = course.students[i];}
} string Course::getCourseName() const
{return courseName;
}void Course::addStudent(const string& name)
{if(student_number >= courseCapacity){cout << "you cat not add student because the course is full." << endl; }    else{students[student_number++] = name;}
}void Course::dropStudent(const string& name)
{bool found_flag = false;int index = 0;for(int i=0; i<student_number-1; i++){if(name==students[i]){found_flag = true;index = i;break;}} if(found_flag){for(int i=index; i<student_number-2; i++){students[i] = students[i+1];}  student_number--;cout << "Student " << name << " is successfully deleted from " << courseName << endl;}else    // 如果学生不在名单中 {cout << "Student " << name << " is not in the course " << courseName << endl;}
}string* Course::getStudents() const
{return students;
}int Course::getNumberOfStudent()   const
{return student_number;
}

main.cpp

#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>   // 将数字转化为字符串
#include <algorithm>
#include "E:\back_up\code\c_plus_code\chapter8\external_file\course.h"using namespace std;void printStudentList(string*, int);int main(int argc, char *argv[])
{Course course1("Mathg", 3);//Course course2("Data Analyse", 4);cout << "course1's name is " << course1.getCourseName() << " and now it has " << course1.getNumberOfStudent() << " students" << endl;course1.addStudent("Tonny");course1.addStudent("Andrea");cout << "course1's name is " << course1.getCourseName() << " and now it has " << course1.getNumberOfStudent() << " students" << endl;cout << "The student list: " << endl;printStudentList(course1.getStudents(), course1.getNumberOfStudent());course1.addStudent("wang hau");cout << "course1 infomation: " << endl;cout << "course1's name is " << course1.getCourseName() << " and now it has " << course1.getNumberOfStudent() << " students" << endl;cout << "The student list: " << endl;printStudentList(course1.getStudents(), course1.getNumberOfStudent());cout << "course2 infomation: " << endl;Course course2(course1);   // course2 copy course1cout << "course2's name is " << course2.getCourseName() << " and now it has " << course2.getNumberOfStudent() << " students" << endl;cout << "The student list: " << endl;printStudentList(course2.getStudents(), course2.getNumberOfStudent());return 0;
}void printStudentList(string* student_list, int num)
{for(int i=0; i<num; i++){cout << student_list[i] << endl;}cout << endl;
}

运行结果:实现了对象的copy

为什么Course类中,要自定义拷贝构造函数,实现深拷贝?

原因::因为course类数据域有指向数组的指针students,如果使用上述的浅拷贝,在copy过程中,两个指针拷贝时,保存了相同地址,即指向了相同的地址。但是在程序执行完毕时,对象需要调用析构函数delete指针students,但是如果两个对象是copy的关系,则会调用两次析构函数删除相同的指针,这是程序就会报错。所以需要自定义拷贝构造函数,实现深拷贝。使两个对象中的数据域中指针students相互独立,就不会出现上述情况。

c++笔记(8) 指针及动态内存管理相关推荐

  1. 《C++应用程序性能优化::第五章动态内存管理》学习和理解

    <C++应用程序性能优化::第五章动态内存管理>学习和理解 说明:<C++应用程序性能优化> 作者:冯宏华等 2007年版. 2010.8.29 cs_wuyg@126.com ...

  2. RT-Thread 动态内存管理(学习笔记)

    本文参考自[野火EmbedFire]<RT-Thread内核实现与应用开发实战--基于STM32>,仅作为个人学习笔记.更详细的内容和步骤请查看原文(可到野火资料下载中心下载) 文章目录 ...

  3. C和C++安全编码笔记:动态内存管理

    4.1 C内存管理: C标准内存管理函数: (1).malloc(size_t size):分配size个字节,并返回一个指向分配的内存的指针.分配的内存未被初始化为一个已知值. (2).aligne ...

  4. 冰冰学习笔记:动态内存管理

    前言: 动态内存管理,顾名思义就是我们可以在具体使用内存空间时,并非一次性的创建完毕,而是根据我们的需求,动态的开辟.申请的空间不够了,开辟一点,空间太大了,那我们就减小一点. 在以前我们开辟空间的办 ...

  5. C++学习笔记-DLL中动态内存管理

    动态内存管理 在dll中malloc的内存,必须在dll中free 注:这是由Windows自己的特点决定! 如果 a 编译成静态库,有下面两种解决方法: 1.b.dll 和 c.dll 使用同一个款 ...

  6. 梓益C语言学习笔记之链表&动态内存&文件

    梓益C语言学习笔记之链表&动态内存&文件 一.定义: 链表是一种物理存储上非连续,通过指针链接次序,实现的一种线性存储结构. 二.特点: 链表由一系列节点(链表中每一个元素称为节点)组 ...

  7. C语言之动态内存管理与动态内存函数

    文章目录 一.为什么存在动态内存分配? 二.动态内存函数的介绍 1.malloc和free 2.calloc函数 3.realloc函数 一.为什么存在动态内存分配? 学习动态内存的管理方法之前,我们 ...

  8. 26.智能指针和动态内存

    在C++中,动态内存的管理是通过一对运算符来完成的.new在动态内存中为对象分配空间并返回一个指向该内存对象的指针. delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存! 为什么要 ...

  9. LwIP 之六 详解动态内存管理 内存池(memp.c/h)

      该文主要是接上一部分LwIP 之 详解动态内存管理 内存堆(mem.c/h),该部分许多内容需要用到上一篇的内容.该部分主要是详细介绍LwIP中的动态内存池.整个内存池的实现相较于内存堆来说,还是 ...

最新文章

  1. go语言学习(4)接口,duck typing
  2. 好嗨哟~谷歌量子神经网络新进展揭秘
  3. 软件工程进度条-第十二周
  4. hql 查询条件 set集合_Redis从入门到深入-Sorted_set的value
  5. Git 有时候推送以及拉去不了代码解决方式(二)
  6. undefinednbsp;referencenbsp;to…
  7. SSM山东医院-1-登录(对象条件查询)-未加密版本
  8. 技术要能够变现才有价值
  9. 由java导入excel表格数据引发的一个惨案…(黑眼圈又重了,头发又白了)
  10. 【机器学习】聚类(Kmeans、MeanShift )
  11. XMPP增加删除好友
  12. 盈高入网规范管理平台linux,入网引导测试和修复测试
  13. 多用户博客BLOG系统大全
  14. Day 16-Vue3 技术_Composition API 的优势
  15. 抖音点亮蓝v多少钱?有啥好处-万顿思电商
  16. CRM百科 | CRM是什么?
  17. [硬件项目] 1、汽车倒车雷达设计——基于API8108A芯片简易智能语音模块的设计与实现...
  18. 索尼z5显示无法连接服务器,索尼手机 z5 为什么上面wifi会显示(!)无法访问网际网路?...
  19. Python学习笔记——绘图设置(二)填色图与等值线图
  20. 《分布式服务框架原理与实践》读书笔记2

热门文章

  1. OAuth2.0_环境搭建_Spring Security OAuth2.0认证授权---springcloud工作笔记139
  2. SQLite学习手册(目录)
  3. 成功在家用ssh远程连上了学校电脑虚拟机当中的ubuntu(代价是虚拟机全部黑屏只能用SSH连接了!)
  4. c++ 十进制、十六进制和BCD的相互转换
  5. swift函数的用法,及其嵌套实例
  6. 随想录(反调试技术)
  7. python编程(数据库操作)
  8. 随想录(简单的windows驱动代码)
  9. 测试cpu的简单工具-dhrystone
  10. java求最大值时i的值_java 输入一组数组,求最大值。