【C++面向对象高级编程】知识点总结(2)

本文深入探讨C++中的面向对象高级编程知识点,包括堆栈的区别、new和delete的内存管理、array new和array delete的使用、static成员变量和函数的特性,以及template模板在类和函数中的应用。详细阐述了new操作的内存分配和构造过程,delete的析构和内存释放流程,以及静态成员的共享属性。同时,讲解了函数模板的参数推导和类模板的实例化过程。
摘要由CSDN通过智能技术生成

书接上回,我们继续来盘点那些面向对象里我们可能知道但并不熟悉的知识点。

需要明白的知识点


关于堆和栈

栈是存在于某作用域的一块内存空间。比如函数本身会形成一个占来接受各种数据。

堆是由操作系统提供的一块全局的内存空间。

简单来说,栈是由编译器自动管理的,而堆则是需要手动释放空间的。

{
	Complex c1(1,2);
	Complex* p=new Complex(3);
}

比如这时c1的空间就是来自栈的,在作用域结束之时结束。

而Complex(3)这个临时对象的空间是new出来的,所以来自堆,需要手动delete,不然等到作用域结束后p就无法找到了,出现内存泄漏。

关于new

new的操作在C++中分为两步:先分配内存,再调用构造函数。

比如对于无指针的Complex类

Complex* pc=new Complex(1,2);

编译器会转化为:

void * mem=operator new(sizeof(Complex));
pc=static_cast<Complex*>(mem);
pc->Complex::Complex(1,2);

首先分配内存, 再将类型转换为需要的Complex,最后调用构造函数。

对于有指针的String类,也是一样的。

String* ps=new String("hello");
void * mem=operator new(sizeof(String));
pc=static_cast<String*>(mem);
pc->String::String("hello");

因为构造指针指向hello内存空间的事情已经交给了构造函数。

关于delete

delete的操作和new刚好相反,也分为两步:先调用析构函数在释放内存。

这里就拿String类举例

String*ps=new String("hello");
...
delete ps;
String::~String(ps);//1
operator delete(ps);//2 free

这里需要明白整个流程,因为delete ps其实和ps指针无关。

我们按顺序说明这个流程。

image-20220117121951855

  1. 首先调用析构,析构是我们写好的delete[]m_data;
  2. 对于这个delete,又会分成两步。这里要析构掉m_data new出来的部分,清空hello。调用的是系统的析构函数。然后free(m_data),也就是回收指针m_data所指向的空间。
  3. 然后回到最外层的第二步,operator delete(ps),本质上是free(ps)。现在ps指向m_data。因为m_data是new出来的,所以需要回收其空间,也就是回收ps所指向的空间。
  4. ps本身不需要回收,它是在栈上的,在作用域结束后就会消失,但需要在消失前处理那些new出来在堆区的部分。

关于array new和array delete

当我们new一个数组类型时,需要使用delete[]配合,而不能使用delete。

String *p = new String[3];
...
delete[] p;

这样会调用三次析构函数,如果使用delete则只会调用一次析构函数,出现错误。

而数组长度是在new时多申请了头部的4个字节存放的,在调用析构的时候就会知晓长度。

那有没有可以不配套使用的情况呢,也是有的。

当类型为int,float等内置类型时,不需要知道长度为多少。可以直接操作内存,就不需要析构函数了。我们在书写时为了保持规范性还是需要配套使用的。

关于static

static成员变量

这里说的static关键字的使用场景是在class中。一般我们定义的class的对象中包含着成员变量。不同的对象的成员变量相互独立,不受影响。

但有时候也有需要数据共享的场景:比如银行类可以定义多个客户,每个客户拥有自己的成员变量。对于银行来说,汇率这个变量就不能定义为对象的成员变量,而应该是属于银行类的变量。

这时候就可以使用static了。static成员变量有几个关键点:

  • static成员变量属于类,不属于具体的对象
  • 必须在类外进行初始化,不初始化无法使用
  • 访问时可以使用对象访问,也可以使用类::变量名直接访问(因为static成员变量不占用对象的内存)

static成员函数

static也可以声明静态成员函数。

  • 普通成员函数可以访问所有成员,而静态成员函数只能访问静态成员

    因为在编译时,普通成员函数会隐式增加形参this,把当前对象的地址赋给this。而静态成员函数是全局的,不需要this,也不知道指向哪个对象,所以只可以访问静态成员。

  • 调用时可以通过对象来调用,也可以通过类来调用。比如Student::getTotal()

关于template模板

类模板

类模板是比较常见的一种用法,在STL中也被大量使用。

template<typename T>
class complex{
public:
	complex(T r=0, T i=0):re(r),im(i){}
...
private:
	T re,im;
}
{
	complex<double>c1(2.0,1.6);
	complex<int>c2(2,1);
}

类模板在调用的时候需要指明类型

函数模板

函数也可以用模板来进行泛化。比如我们有一个石头类,这个类也想使用min函数来比大小,那么min就可以用函数模板来写。

template <class T>
inline
const T& min(const T& a,const T& b)
{
	return b<a?b:a;
}

但这个时候石头类是不知道<怎么比大小,所以需要重载符号

class stone
{
public:
	stone(int w,int h,int we)
	:_w(w),_h(h),_weight(we){}
	bool operator< (const stone& rhs) const
	{
		return _weight<ths._weight;
	}
private:
	int _w,_h,_weight;
};

实际调用时也不需要声明类型,编译器可以对函数模板进行参数推导

stone r1(2,3),r2(3,3),r3;
r3=min(r1,r2);//r3=min<stone>(r1,r2);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值