2.new与delete、重载函数

目录

 

域运算符

new和delete

重载

name managling与extern“C”

带默认形参值的函数


域运算符

C++中增加的作用域标识符::

  • 用于对与局部变量同名的全局变量进行访问
  • 用于表示类的成员
int var = 100;
int main(void)
{
	int var = 50;
	cout<<var<<endl; //输出50
	cout<<::var<<endl; //输出100
	return 0;
}

new和delete

new运算符可以用于创建堆空间,成功返回首地址

语法:

指针变量=new数据类型;    指针变量=new数据类型[长度n];

例如:

  int *p; p=new int;

  char*pStr=new char[50];

delete运算符可以用于释放堆空间

语法:

 delete指针变量;                delete []指针变量;

例如:

  deletep;

  delete[] pStr;

注意 使用new [] 和则一定要使用delete [],否则释放的结果不确定

如果要初始化一个new对象则可以这样 

int *p=new int(6);

new分配一个对象实际上做了两件事情:一是分配内存空间 二是调用构造函数

delete释放一个对象实际上做了两件事情:一是调用析构函数,二是释放内存

new的三种用法

  • new operator  分配内存+调用构造函数
  • opreator new  只分配内存
  • placement new 不分配内存,只调用拷贝构造函数

new和malloc的区别

  1. 申请内存所在位置:new操作符从由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。特别的,new甚至可以不为对象分配内存!placement new的功能可以办到这一点,定位new允许我们在一个特定的、预先分配的地址上构造对象。 

  2.  返回类型安全性:new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
  3. 内存分配失败返回值:new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL,因此没有必要与NULL比较,正确的做法是使用异常机制;malloc分配内存失败时返回NULL。在使用C语言时,我们习惯在malloc分配内存后判断分配是否成功。
try
{
    int *a = new int();
}
catch (bad_alloc)
{
    ...
}

    4.是否需要指定内存大小:使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc则需要显式地指出所需内存的尺寸。

     5.使用new操作符来分配对象内存时会经历三个步骤:

  • 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
  • 第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
  • 第三部:对象构造完成后,返回一个指向该对象的指针。

使用delete操作符来释放对象内存时会经历两个步骤:

  • 第一步:调用对象的析构函数。
  • 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。

     new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会,只负责分配空间。

  6. new调用malloc:operator new /operator delete的实现可以基于malloc,而malloc的实现不可以去调用new。下面是编写                   operator new /operator delete 的一种简单方式,其他版本也与之类似:

void * operator new (sieze_t size)
{
    if(void * mem = malloc(size)
        return mem;
    else
        throw bad_alloc();
}
void operator delete(void *mem)noexcept{
    free(mem);
}

7.重载:C++允许”重载new/delete操作符”,实际上是提供新的operator new和operator delete函数改变内存分配方式。特别的,placement new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。

8.重新分配内存:使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。new没有这样直观的配套设施来扩充内存。

9.newhandler: 在operator new抛出异常以反映一个未获得满足的需求之前,它会先调用一个用户指定的错误处理函数,这就是new-handler。new_handler是一个指针类型:

namespace std
{
    typedef void (*new_handler)();
}

指向了一个没有参数没有返回值的函数,即为错误处理函数。为了指定错误处理函数,客户需要调用set_new_handler,这是一个声明于的一个标准库函数:

namespace std
{
    new_handler   set_new_handler(new_handler p ) throw();
}

set_new_handler的参数为new_handler指针,指向了operator new 无法分配足够内存时该调用的函数。其返回值也是个指针,指向set_new_handler被调用前正在执行(但马上就要发生替换)的那个new_handler函数。对于malloc,客户并不能够去编程决定内存不足以分配时要干什么事,只能看着malloc返回NULL。

 

重载

相同的作用域,如果两个函数名称相同,而参数不同,我们把它们称为重载overload

函数重载又称为函数的多态性,这种多态性称为静态的多态性。动态的多态性表现为派生类并且基类为虚函数,这是一种动态绑定。静态的多态在编译时确定函数的入口地址,称为静态联编。动态的多态在运行时决定函数的入口地址,也称为动态联编。

函数重载不同形式:

  • 形参数量不同
  • 形参类型不同
  • 形参的顺序不同
  • 形参数量和形参类型都不同

调用重载函数时,编译器通过检查实际参数的个数、类型和顺序来确定相应的被调用函数

合法的重载例子:

int abs(inti);
long abs(long l);
double abs(double d);

非法的重载例子:

int abs(int i);
long abs(int i);
void abs(int i);

//如果返回类型不同而函数名相同、形参也相同,则是不合法的,编译器会报"语法错误"。

name managling与extern“C”

  • name managling这里把它翻译为名字改编。
  • C++为了支持重载,需要进行name managling
  • extern “C”实现C与C++混合编程,extern "C"表示不进行名字改编,按C语言的方式解释函数名,C语言不支持函数重载,如果希望写的函数能够被C语言调用,加上extern "C",如:
    extern "C" void fun(int a);
    注意加上extern "C"  后不支持重载
#ifdef __cpluscplus
extern “C”
{
#endif
...
#ifdef __cpluscplus
}
#endif
#ifdef __cpluscplus
extern "C"
{
#endif
void fun(int a)
{
	cout<<"xxxx"<<endl;
}
void fun2(double a)
{
	cout<<"yyyy"<<endl;
}
#ifdef __cpluscplus
}
#endif

 

带默认形参值的函数

  • 函数声明或者定义的时候,可以给形参赋一些默认值
  • 调用函数时,若没有给出实参,则按指定的默认值进行工作
  • 函数没有声明时,在函数定义中指定形参的默认值
  • 函数既有定义又有声明时,声明时指定后,定义后就不能再指定默认值
  • 默认值的定义必须遵守从右到左的顺序,如果某个形参没有默认值,则它左边的参数就不能有默认值。
  • 函数调用时,实参与形参按从左到右的顺序进行匹配
void func1(inta, double b=4.5, int c=3); //合法
void func1(inta=1, double b, int c=3); //不合法

重载的函数中如果形参带有默认值时,可能产生二义性

int add(int x=5, int y=6); 
int add(int x=5, int y=6, int z=7); 
int main() { 
	int sum;
	sum= add(10,20); 
	return 0;
}
int add(int x, int y) 
{ 
       return x+y; 
} 
int add(int x,  int y, int z) 
{ 
       return x+y; 
}

sum=add(10,20)语句产生二义性性,可以认为该语句是调用第一个函数,也可以是第二个,因此编译器不能确定调用的是哪一个函数

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值