第14课 - 专题二经典问题解析
一.malloc与free和new与delete有什么区别?
(函数) (关键字)
1.1 new在申请的时候可以直接初始化
Source Example 1.1:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int main()
{
int* p = reinterpret_cast<int *>(malloc(sizeof(int)));
/* new关键字可以在申请的时候初始化 */
int *q = new int(10);
*p = 5;
//*q = 10;
/* 输出5 10 */
cout<<*p<<endl<<*q<<endl;
return 0;
}
1.2 new和delete在申请类空间时负责构造函数和析构函数的调用
Source Example 1.2
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class Test{
private:
int i;
public:
Test()
{
cout<<"Test()"<<endl;
i = 0;
}
int GetI()
{
return i;
}
~Test()
{
cout<<"~Test()"<<endl;
}
};
int main()
{
Test* p = reinterpret_cast<Test *>(malloc(sizeof(Test)));
Test *q = new Test;
/* 输出一个随机数,没有调用构造函数,并不是一个对象 */
cout<<p->GetI()<<endl;
/* 输出0,表明调用了构造函数 */
cout<<q->GetI()<<endl;
/* 如果用delete函数也会调用析构函数 */
free(p);
/* 会调用析构函数,也可以用free函数来释放,但是会没有析构函数的调用 */
delete q;
return 0;
}
输出结果如下:
1.3总结:
1.3.1 malloc和free是库函数,以字节为单位申请堆内存
1.3.2 new和delete是关键字,以类型为单位申请堆内存
1.3.3 malloc和free单纯的对内存进行申请与释放
1.3.4 对于基本类型new关键字会对内存进行初始化
1.3.5 对于类类型new和delete还负责构造函数和析构函数的调用
二.编译器对构造函数的调用
2.1 C++编译器会尝试各种手段尝试让程序通过编译
2.1.1 方式一: 尽力匹配重载函数
2.1.2 方式二: 尽力使用函数的默认参数
2.1.3 方式三:尽力尝试调用构造函数进行类型转换
Source Example2.1:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class Test{
private:
int i;
public:
Test(int i)
{
cout<<"Test(int)"<<i<<" "<<endl;
}
Test(const Test& t)
{
cout<<"Test(const Test& t)"<<endl;
}
~Test()
{
cout<<"~Test()"<<endl;
}
};
void func()
{
/*
* "古代"编译器的处理
* 对于Test t2 = 5; 分析
* 1. 默认情况下,字面量5的类型为int,因此5无法直接初始化Test对象
* 2. 但是编译器在默认情况下可以自动调用构造函数,于是编译器就会将5作为参数调用构造函数
* 3. 调用Test(int)生成一个临时对象
* 4. 将临时对象用来初始化定义的对象会调用拷贝构造函数
*
* 对于Test t3 = Test(5);分析
* 1. 直接调用Test(int)生成一个临时对象
* 2. 将临时对象用来初始化定义的对象会调用拷贝构造函数
*
* 每次都要创建一个临时对象,效率低下
*
* "现代"编译器会对后两种初始化方式进行优化
* Test t1 = 5 <==> Test t1(5);
* 不会再去创建临时对象,因此这三种初始化方式是等价的
*/
Test t1(5);
Test t2 = 5;
Test t3 = Test(5);
}
int main()
{
func();
return 0;
}
输出结果如下:
由于是现代编译器进行编译,并不会创建临时对象,不会去调用拷贝构造函数。
三. "剥夺"编译器对构造函数的调用尝试
3.1 C++提供了explicit关键字用于阻止编译器对构造函数的调用尝试
Source Example 3.1:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class Test{
private:
int i;
public:
/* 剥夺了编译器调用构造函数的权利 */
explicit Test(int i)
{
cout<<"Test(int)"<<i<<" "<<endl;
}
explicit Test(const Test& t)
{
cout<<"Test(const Test& t)"<<endl;
}
~Test()
{
cout<<"~Test()"<<endl;
}
};
void func()
{
/* 初始化的标准写法,确实想要用构造函数构造这个对象(我们主动调用的),可以调用 */
Test t1(5);
/* 将5赋值给t2,编译器发现不对会去尝试调用构造函数,但是无法调用,因此会报错 */
Test t2 = 5;
/*
* 我们主动调用的,不会报错
* 但是需要调用拷贝构造函数进行赋值,但是无法调用,仍然会出错
*/
Test t3 = Test(5);
}
int main()
{
func();
return 0;
}
四.类的静态成员能干啥呢?
4.1 对象数目控制,一个类最多只有一个对象存在系统中,如何实现?
一个汽车类只有一个引擎对象
Source Example 4.1:(单例模式的实现)
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class SingnaleTon{
private:
static SingnaleTon* pInstance;
SingnaleTon()
{
}
public:
static SingnaleTon* GetSignaleTon()
{
if (pInstance == NULL)
{
/* 只被打印了一次 */
cout<<"I am GetSignaleTon"<<endl;
pInstance = new SingnaleTon;
}
return pInstance;
}
~SingnaleTon()
{
pInstance = NULL;
}
};
SingnaleTon* SingnaleTon :: pInstance = NULL;
void func()
{
SingnaleTon* s1 = SingnaleTon :: GetSignaleTon();
SingnaleTon* s2 = SingnaleTon :: GetSignaleTon();
SingnaleTon* s3 = SingnaleTon :: GetSignaleTon();
/* 编译这句会报错,因为new的时候要调用构造函数,但是构造函数时私有数据 */
//SingnaleTon* s3 = new SingnaleTon;
/* 打印的s1,s2,s3的地址相同 */
cout<<s1<<" "<<s2<<" "<<s3<<" "<<endl;
}
int main()
{
func();
return 0;
}
五.无状态和状态函数
5.1 无状态函数
函数的调用结果只与实参值相关
Source Example 5.1:
/* 每一项都会做重复循环,时间复杂度为O(n) */
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;
}
5.2 状态函数
函数的调用结果不仅与实参值相关还与之前的函数调用相关
Source Example5.2:
/* 第n次调用返回斐波那契数列的第n项 */
/* 时间复杂度为O(1),但是无法从头在来 */
int fib2()
{
static int a1 = 0;
static int a2 = 1;
int ret = a1 + a2;
a1 = a2;
a2 = ret;
return ret;
}
5.3 函数对象的实现
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
int fib1(int i)
{
int a1 = 0;
int a2 = 1;
int ret = 1;
while (i >= 1)
{
ret = a1 + a2;
a1 = a2;
a2 = ret;
i--;
}
return ret;
}
int fib2()
{
static int a1 = 0;
static int a2 = 1;
int ret = a1 + a2;
a1 = a2;
a2 = ret;
return ret;
}
class Fib {
private:
int a1;
int a2;
public:
Fib()
{
a1 = 0;
a2 = 1;
}
/* 函数操作符重载! */
int operator() ()
{
int ret = a1 + a2;
a1 = a2;
a2 = ret;
return ret;
}
};
int main()
{
Fib fib;
/* 效率不够 */
for (int i = 1; i < 10; i++)
{
printf ("%d\n", fib1(i));
}
/* 无法从头再来 */
for (int i = 1; i < 10; i++)
{
printf ("%d\n", fib2());
}
/* 再定义一个对象即可从头在来 */
for (int i = 1; i < 10; i++)
{
printf ("%d\n", fib());
}
return 0;
}