视频33——动态内存分配
静态内存
在完成它的任务时所使用的内存空间都是固定不变的,不能在程序运行期间动态增加或减少内存空间
动态内存
动态内存由一些没有名字、只有地址的内存块构成,那些内存块是在程序运行期间【和静态内存的主要区别】动态分配的。他们来自一个由标准C++库替你管理的大池子(术语“内存池”),从内存池申请一些内存需要用到new语句,它将根据你提供的数据类型分配一块大小适当的内存。
在使用动态内存时,最重要的原则是每一条new语句都必须有一条与之配对的delete语句,否则将导致内存泄漏。
delete语句只释放给定指针变量正指向的内存块,不影响这个指针。在执行delete语句之后,那个内存块被释放了,但指针变量还依然存在。所以一般保险起见需要设置i = NULL
#include <iostream>
using namespace std;
#include <string>
基类
class Company
{
public:
//构造器
Company(string theName);
//方法
virtual void printInfor();//打印信息
protected:
string name;
};
//基类构造器实现
Company::Company(string theName)
{
name = theName;
}
//基类方法实现
void Company::printInfor()
{
cout << "这家公司的名字叫:" << name << endl;
}
子类
class TechCompany:public Company
{
public:
//构造器
TechCompany(string theName, string product);
//方法
void printInfor();//打印信息
private:
string product;
};
//构造器实现
TechCompany::TechCompany(string theName, string product):
Company(theName)
{
this->product = product;
}
//方法实现
void TechCompany::printInfor()
{
cout << name << "公司生产了" << product << "\n\n";
}
int main()
{
Company *company = new Company("苹果");
company->printInfor();
delete company;//删除指针地址
company = NULL;//为指针指向的地址赋空值
company = new TechCompany("APPLE", "IPHONE");
company->printInfor();
delete company;//删除指针地址
return 0;
}
视频34——动态数组
虽然前面讲过的用new给基本类型和对象在运行时分配内存,但它们的尺寸在编译时就已经确定下来——因为我们为之申请内存的数据类型在程序中有明确的定义,有明确的单位长度;但有些时候,必须等到程序运行时才能确定需要申请多少内存,甚至还需要根据程序的运行情况追加申请更多的内存。例如: int *x = new int[10];//x表示整型数组的数组名
删除动态数组
#include <iostream>
#include <string>
using namespace std;
int main()
{
unsigned int count = 0;
cout << "请输入数组的元素个数:" << '\n';
cin >> count;
//程序运行时才申请,不是在编译的时候,所以叫做动态的
//在堆里面申请,不是在栈【一般局部变量在栈中申请】里申请 。
int *x = new int[count];
//数组赋值
for(int i = 0; i< count; i++)
{
x[i] = i;
}
//输出打印
for(int i = 0; i< count; i++)
{
cout << "x[" << i << "] = " << x[i] << endl;
}
return 0;
}
PS:(1)动态数组:程序运行时才申请,不是在编译的时候,所以叫做动态的;在堆里面申请,不是在栈【一般局部变量在栈中申请】里申请 。
视频35——从函数或方法返回内存
基本思路:在函数里调用new语句为某种对象或某种基本数据类型分配一块内存,再把那块内存的地址返回给程序的主代码,主代码将使用那块内存并在完成有关操作后立刻释放delete。
#include <iostream>
using namespace std;
int *newInt(int value)
{
int *myInt = new int;
*myInt = value;
return myInt;
}
int main()
{
int *x = newInt(20);
cout << *x;
delete x;
x = NULL;
return 0;
}
#include <iostream>
using namespace std;
void swap(int *x,int *y)
{
//永远不会被执行;想要执行的话把0换为1
#if 0
int temp;
temp = *x;
*x = *y;
*y = temp;
#endif
//^异或运算符
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
int main()
{
int a,b;
a = 3;
b = 5;
swap(&a,&b);
cout << "a = " << a
<< ", b = " << b << endl;
return 0;
}
PS:“#if 0 ……………………#endif”语句功能和“/*……………………*/”一样。
函数指针&指针函数
函数指针:指向函数首地址的指针变量称为函数指针。
指针函数:一个函数可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。
/**< 函数指针 */
#include <stdio.h>
int fun(int x,int y)
{
int z;
z = (x > y)? x:y;
return z;
}
int main()
{
int i,a,b;
//声明函数指针
//函数如何声明参数【类型、个数】那么函数指针就应该如何声明参数,否则会出错。
int (*p)(int,int);
p = fun;//给函数指针p赋值,使其指向函数fun
printf("请输入10个数字:\n");
scanf("%d", &a);
for(i = 0; i< 9; i++)
{
scanf("%d", &b);
a = (*p)(a,b);//通过指针p调用指针函数fun
}
printf("The Max number is %d.", a);
return 0;
}
PS:函数指针的参数变量如何声明取决于相应函数参数变量如何声明,包括参数变量的类型和个数。eg:上述程序中函数声明为int fun(int x,int y);那么其函数指针声明为int (*p)(int,int);不可以是int (*p)();
/**< 指针函数 */
#include<iostrem>
using namespace std;
//声明一个指针函数,返回一个地址
int *newInt(int value)
{
int *myInt = new int;
*myInt = value;
return myInt;
}
int main()
{
//新建一个整型变量空间,并将x指针指向这个空间
int *x = newInt(20);
cout << *x;
delete x;
x = NULL;
return 0;
}
视频36——副本构造器
我们可以把一个对象赋值给一个类型与之相同的变量,编译器将生成必要的代码把“源”对象各属性的值分别赋值给“目标”对象的对应成员。这种赋值行为称之为逐位复制。(但如果某些成员变量是指针的话,对象成员逐位复制的结果是你将拥有两个一模一样的实例,而这两个副本里的同名指针会指向相同的地址)
/**< 带指针变量的等号重载 */
#include <iostream>
using namespace std;
class MyClass
{
public:
//构造器
MyClass(int *p);
//析构器
~MyClass();
//运算符“="重载
MyClass &operator = (const MyClass &rhs);
void print();
private:
int *ptr;
};
//构造器实现
MyClass::MyClass(int *p)
{
ptr = p;
}
//析构器实现
MyClass::~MyClass()
{
delete ptr;
}
//运算符(=)重载实现,rhs为另一对象
MyClass &MyClass::operator=(const MyClass &rhs)
{//obj1不等于obj2时,this指针指的是指向当前类生成的对象(此处指第一个MyClass)
if(this != &rhs)
{
delete ptr;//删除地址ptr,释放obj1的内存
ptr = new int;//创建新的内存给指针ptr
*ptr = *rhs.ptr;//解引用,将obj2的值赋值给obj1
}
else
{
cout << "赋值号两边为同个对象,不做处理!\n";//obj1 = obj2时
}
return *this;//返回第一个MyClass对象
}
void MyClass::print()
{
cout << *ptr << endl;
}
int main()
{
MyClass obj1(new int(1));//创建对象obj1
MyClass obj2(new int(2));//创建对象obj2
obj1.print();//打印对象obj1中指针指向的地址的储存值
obj2.print();//打印对象obj2中指针指向的地址的储存值
obj2 = obj1;//obj1通过等号重载给obj2
obj1.print();//打印对象obj1中指针指向的地址的储存值
obj2.print();//打印对象obj2中指针指向的地址的储存值
return 0;
}
/**< 副本构造器 */
#include <iostream>
using namespace std;
class MyClass
{
public:
//主构造器【不需要返回类型】
MyClass(int *p);
//副本构造器
MyClass(const MyClass &rhs);
//析构器
~MyClass();
//运算符(=)重载,对象复制,括号里声明对象调用副本构造器
MyClass &operator = (const MyClass &rhs);
void print();
private:
int *ptr;
};
//主构造器实现
MyClass::MyClass(int *p)
{
cout << "进入主构造器\n";
ptr = p;
cout << "离开主构造器\n";
}
//副本构造器实现
MyClass::MyClass(const MyClass &rhs)
{
cout << "进入副本构造器\n";
*this = rhs; //等号赋值重载
cout << "离开副本构造器\n";
}
//析构器实现
MyClass::~MyClass()
{
cout << "进入析构器\n";
delete ptr;
cout << "离开析构器\n";
}
//运算符(=)重载实现,rhs为另一对象
MyClass &MyClass::operator=(const MyClass &rhs)
{
cout << "进入赋值语句重载\n";
//obj1不等于obj2时,this指针指的是指向当前类生成的对象(此处指第一个MyClass)
if(this != &rhs)
{
delete ptr;//删除地址ptr,释放obj1的内存
ptr = new int;//创建新的内存给指针ptr
*ptr = *rhs.ptr;//解引用,将obj2的值赋值给obj1
}
else//obj1与obj2为同一个对象时
{
cout << "赋值号两边为同个对象,不做处理!\n";//obj1 = obj2时
}
cout << "离开赋值语句重载\n";
return *this;//返回第一个MyClass对象
}
void MyClass::print()
{
cout << *ptr << endl;
}
int main()
{
MyClass obj1(new int(1));//创建对象obj1
MyClass obj2(new int(2));//创建对象obj2
obj2 = obj1;//obj1通过等号重载给obj2
obj1.print();//打印对象obj1中指针指向的地址的储存值
obj2.print();//打印对象obj2中指针指向的地址的储存值
cout << "---------------------------------------------\n";
MyClass obj3(new int(3));//创建对象obj3
MyClass obj4 = obj3;
obj3.print();//打印对象obj3中指针指向的地址的储存值
obj4.print();//打印对象obj4中指针指向的地址的储存值
cout << "---------------------------------------------\n";
MyClass obj5(new int(5));//创建对象obj5
obj5 = obj5;
obj5.print();//打印对象obj5中指针指向的地址的储存值
cout << "---------------------------------------------\n";
return 0;
}
程序运行结果