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