C++--第14课 - 专题二经典问题解析


第14课 - 专题二经典问题解析

1. malloc与free和new与delete有什么区别?

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))); //在堆上申请空间

//reinterpret_cast是C++中强制类型转换的关键字

int* q = new int(10); //在堆上申请空间

*p = 5;

//*q = 10;

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

free(p);

delete q;

//仅从上面的两段程序来看,new关键字只是比malloc关键字多了可以在初始化时赋值

Test* op = reinterpret_cast<Test*>(malloc(sizeof(Test)));

Test* oq = new Test;

cout<<op->getI()<<" "<<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;

}

运行结果:

5 10

Test<>

6230240 0

~Test

op指向一段内存空间,但是没有正常调用构造函数。因为malloc不具备初始化的功能,所以malloc函数肯定也不能调用构造函数。但是new是可以调用构造函数的。

注意:

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

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

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

(4) 对于基本类型new关键字会对内存进行初始化。

(5) 对于类类型new和delete还负责构造函数和析构函数的调用。

2. 编译器对构造函数的调用

#include <cstdlib>

#include <iostream>

using namespace std;

class Test

{

public:

Test(int i)

{

cout<<"Test(int i)"<<endl;

}

Test(const Test& obj) //拷贝构造函数

{

cout<<"Test(const Test& obj)"<<endl;

}

~Test()

{

cout<<"~Test"<<endl;

}

};

void func()

{

Test t1(5); //最标准的初始化方式

Test t2 = 5; //手工赋值

Test t3 = Test(5);

}

int main(int argc, char *argv[])

{

func();

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

运行结果:

Test<int i>

Test<int i>

Test<int i>

~Test

~Test

~Test

在现在的C++编译器看来,以上的三种初始化方式是一样的。

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

(1) 方式一:尽力匹配重载函数。

(2) 方式二:尽力使用函数的默认参数。

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

l 对于我们上面的例子Test t2 = 5;

(1) 默认情况下,字面量5的类型为int,因此5无法直接用于初始化Test对象;

(2) 但是编译器在默认情况下可以自动调用构造函数;

(3) 于是编译器尝试调用Test(int)生成一个临时对象;

(4) 之后调用拷贝构造函数Test(const Test&)用临时对象对t2进行初始化。

l 对于以前的编译器

方案A:Test t1 = 5;  Test t1 = Test(Test(5));

这是最开始C++使用的编译方法,显然这种方法是效率底下的,所以我们使用了下面的这种方法。

方案B:Test t1 = 5;  Test t1(5);

经过了这种方式而定优化,我们看到了我们使用上面三种方式的初始化方法都是一样的,并没有调用拷贝函数这个步骤。

3. explicit关键字

“剥夺”编译器对构造函数的调用尝试。C++提供了explicit关键字用于阻止编译器对构造函数的调用尝试。

例如:

class Test

{

public:

explicit Test(int i)

{

cout<<"Test(int i)"<<endl;

}

explicit Test(const Test& obj) //拷贝构造函数

{

cout<<"Test(const Test& obj)"<<endl;

}

~Test()

{

cout<<"~Test"<<endl;

}

};

当我们在上面的例子里面使用上面的程序,我们会发现运行的时候会出错。因为我们剥夺了编译器主动调用构造函数的权利。

4. 静态成员能用来做什么

对象数目控制,一个类最多只能有一个对象存在于系统中,如何实现?

现实生活中就是一个汽车只能配备自己的发动机一样。

程序—单例模式的实现

#include <cstdlib>

#include <iostream>

using namespace std;

class Singleton

{

private:

static Singleton* cInstance;

Singleton() /*构造函数是私有的,函数不能在外部被调用。但是因为这个类

只有一个对象,所以我们用静态的方法来定义指针*/

{

}

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(); //s这个指针指向成员函数中的返回值

Singleton* s1 = Singleton::GetInstance();

Singleton* s2 = Singleton::GetInstance();

/*调用了三次 Singleton::GetInstance()函数,但是 new Singleton()只打印了一次

而且三次的地址都是一样的,这就是工程里面常用的单列函数*/

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;

}

运行结果:

new Singleton()

0x5e10c0 0x5e10c0 0x5e10c0

I’m Singleton!

5. 两种特殊的函数

这两种函数在正常的C语言和C++中是不使用的,但是我们还是要介绍一下。

无状态函数:函数的调用结果只与实参值相关。

状态函数:函数的调用结果不仅与实参值相关还与之前的函数调用有关。

#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()

/*状态函数,在函数的内部使用了两个静态局部变量,每次调用都会改变静态局部变量的

值。它的功能是第n次调用就返回斐波那契数列的第n项*/

{

static int a1 = 0;

static int a2 = 1;

int ret = a2;

int t = a2;

a2 = a2 + a1;

a1 = t;

return ret;

}

int main(int argc, char *argv[])

{

for(int i=1; i<=10; i++)

{

cout<<fib1(i)<<endl;

}

for(int i=1; i<=10; i++)

{

cout<<fib2()<<endl;

}

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

分析:

l fib1是以无状态函数的方式实现的,求解数列每一项时都会做重复的循环,时间复杂度为O(n)。

l fib2是以状态函数的方式实现的,每调用一次就可以得到数列当前项的值,时间复杂度为O(1),但是无法从头再来。

我们现在来想,是否有一个折中的方法,既可以提高效率又可以降低复杂度。C语言中这种方式是实现不了的,但是在C++中这是可以实现的。我们具体来看一下下面的程序。

l 程序—函数对象的实现,通过重载来实现。

#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和a2赋初值。

{

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;

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

for(int i=1; i<=10; i++)

{

cout<<fib()<<endl;/*fib是一个对象,我们能像一个函数一样来使用吗?

我们正常来写就是fib().operator(),简写就是我们程序中的样子*/

}

cout<<endl;

cout << "Press the enter key to continue ...";

cin.get();

return EXIT_SUCCESS;

}

优质内容筛选与推荐>>
1、C语言提高 (6) 第六天 文件(续) 链表的操作
2、python爬虫实例--博客园首页Java目录博文爬虫
3、临时表问题
4、echats--》饼图 如何在环形中央设置 文字?
5、郁闷


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号