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

来源:互联网 发布:sql server 分组求和 编辑:程序博客网 时间:2024/05/14 12:42

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;}
0 0