您的位置:首页 > 其它

经典问题解析(2)---malloc和new、编译器和构造函数、静态成员

2017-02-23 15:39 369 查看

malloc与free和new与delete有什么区别

程序:

#include <cstdlib>
#include <iostream>
using namespace std;

class Test
{
private:
int i;
public:
Test()
{
cout<<"Test()"<<endl;
i = 0;
}
Test(int i)
{
cout<<"Test(int i)"<<endl;
this->i = i;
}
~Test()
{
cout<<"~Test"<<endl;
}
int getI()
{
return i;
}
};

void func()
{
int* p = reinterpret_cast<int*>(malloc(sizeof(int)));
//malloc返回的指针为void*类型,需要强制类型转换,申请4个字节的空间
//malloc只能申请空间,不能进行初始化
int* q = new int(10);
//定义了一个int变量,值为10 ,new可以做到在申请空间时进行初始化

*p = 5;
//*q = 10;

cout<<*p<<" "<<*q<<endl;

free(p);
delete q;

cout<<"op:"<<endl;
Test* op = reinterpret_cast<Test*>(malloc(sizeof(Test)));
//没有打印出构造函数中的内容
//malloc只是负责单纯的申请空间,并不负责调用构造函数

cout<<"oq:"<<endl;
Test* oq = new Test;
//打印出了构造函数的内容
//new不但申请了空间,而且把这段空间构造成了一个Test类的对象

cout<<"op:"<<op->getI()<<" "<<"oq:"<<oq->getI()<<endl;

free(op);
delete oq;
}

int main(int argc, char *argv[])
{
func();

cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}


1、malloc和free是库函数,以字节为单位申请和释放堆内存。

2、new和delete是关键字,以类型为单位申请和释放堆内存。

3、malloc和free单纯的对内存进行申请与释放。

4、对于基本类型new关键字会对内存进行初始化,为变量赋初值,为类对象调用构造函数。

5、对于类类型new和delete还负责构造函数和析构函数的调用。因此对于new分配的空间,不能使用free进行释放,否则没有析构会造成内存泄漏。

编译器对构造函数的调用



现代的 C++编译器看来,上面func函数中的3中类对象的初始化方式是相同的。

C++编译器会尝试各种手段尝试让程序通过编译

方式一:尽力匹配重载函数

方式二:尽力使用函数的默认参数

方式三:尽力尝试调用构造函数进行类型转换



因为拷贝构造函数总是存在的,因此上面的用一个类对象给另一个类对象赋值,总是成功的。

但是一般编译器会采用一种更简单的方式:



编译器会采用方案B的方法,因此上面的程序的运行结果中,没有拷贝构造函数的打印输出。

因此,现代的编译器会把上面的4种方式进行优化,优化为方案B。

“剥夺”编译器对构造函数的调用尝试:

C++提供了explicit关键字用于阻止编译器对构造函数的调用尝试,如果加上explicit关键字,上面的程序会报错。



如果程序这样写,就会产生以下结果:

Test t1(5);//不报错,这是标准的初始化写法
Test t2 = 5;//报错
Test t3 = Test(5);//报错


类的静态成员

对象数目控制需要用到类的静态成员。

单例模式的实现:

#include <cstdlib>
#include <iostream>
using namespace std;
//实现单例模式
class Singleton
{
private:
static Singleton* cInstance;//定义一个指针

Singleton()//将构造函数定义为private
{
}
public:
static Singleton* GetInstance()//静态成员函数可以访问静态成员变量和其他成员函数
{
if( cInstance == NULL )//只能申请一次
{
cout<<"new Singleton()"<<endl;
cInstance = new Singleton();//创建一个类对象
}

return cInstance;
}

void print()
{
cout<<"I'm Singleton!"<<endl;
}
};

Singleton* Singleton::cInstance = NULL;

void func()
{
Singleton* s = Singleton::GetInstance();
Singleton* s1 = Singleton::GetInstance();
Singleton* s2 = Singleton::GetInstance();

cout<<s<<" "<<s1<<" "<<s2<<endl;//三个指针指向的的地址相同

s->print();
}

int main(int argc, char *argv[])
{
func();

cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}


无状态函数:

函数的调用结果只与实参值相关。但是多次调用会浪费时间。

状态函数:

函数的调用结果不仅与实参值相关还与之前的函数调用有关。但是不能回头。

函数对象来实现结合无状态函数和状态函数的优点:

#include <cstdlib>
#include <iostream>
using namespace std;
//三种方法实现斐波那契数列

int fib1(int i)//无状态的函数,比较浪费时间
{
int a1 = 0;
int a2 = 1;
int ret = a2;
while( i > 1)
{
ret = a2 + a1;
a1 = a2;
a2 = ret;
i--;
}
return ret;
}

int fib2()//有状态的函数,可以节约计算时间,但是不能回头
{
static int a1 = 0;
static int a2 = 1;

int ret = a2;
int t = a2;
a2 = a2 + a1;
a1 = t;
return ret;
}

class Fib
{
private:
int a1;
int a2;
public:
Fib()
{
a1 = 0;
a2 = 1;
}

int operator() () //重载()操作符
{
int ret = a2;
int t = a2;

a2 = a2 + a1;
a1 = t;

return ret;
}
};

int main(int argc, char *argv[])
{
cout<<"int fib1(int i)"<<endl;

for(int i=1; i<=10; i++)
{
cout<<fib1(i)<<endl;
}

cout<<endl;

cout<<"int fib2()"<<endl;

for(int i=1; i<=10; i++)
{
cout<<fib2()<<endl;
}

cout<<endl;

Fib fib;//采用类对象,可以回头从1开始,也可以记录状态

cout<<"Fib fib;"<<endl;

for(int i=1; i<=10; i++)
{
cout<<fib()<<endl;//调用重载操作符
}

cout<<endl;

cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息