C++深度解析教程笔记15
本文学习自狄泰软件学院 唐佐林老师的 C++深度解析教程,图片全部来源于课程PPT,仅用于个人学习记录
第66课 - C++ 中的类型识别
期望指针指向的对象的类型 静态类型
实际指针可能指向子类对象,此类型为动态类型
解决方案一利用多态
1.在基类中定义虚函数返回具体的类型信息
2.所有的派生类都必须实现类型相关的虚函数
3.每个类中的类型虚函数都需要不同的实现
C++中如何得到动态类型?
实验-动态类型获取:利用多态
#include <iostream>
#include <string>
using namespace std;
class Base//父类 基类
{
public:
virtual string type()//虚函数
{
return "Base";
}
};
class Derived : public Base//子类1
{
public:
string type()
{
return "Derived";
}
void printf()
{
cout << "I'm a Derived." << endl;
}
};
class Child : public Base//子类2
{
public:
string type()
{
return "Child";
}
};
void test(Base* b)
{
/* 危险的转换方式 */
// Derived* d = static_cast<Derived*>(b);
//不判断动态类型:case1
// Derived* d = static_cast<Derived*>(b);
//d->printf();
/*
if( b->type() == "Derived" )//判断动态类型case2
{
Derived* d = static_cast<Derived*>(b);
d->printf();
}*/
//case3:
Derived *d=dynamic_cast<Derived*>(b);
cout << d << endl;
if(d!=0)d->printf();
else cout << "NULL"<< endl;
}
int main(int argc, char *argv[])
{
Base b;
Derived d;
Child c;
test(&b);
test(&d);
test(&c);
/*
不判断动态类型:
I'm a Derived.
I'm a Derived.
I'm a Derived.
判断动态类型:
I'm a Derived.
dynamic_cast:(只能判断转换是否成功,不能获取动态类型)
0
NULL
0x61fe10
I'm a Derived.
0
NULL
*/
return 0;
}
动态类型识别
■多态解决方案的缺陷
一必须从基类开始提供类型虚函数
一所有的派生类都必须重写类型虚函数
一每个派生类的类型名必须唯一
类型识别关键字
■C++提供了typeid关键字用于获取类型信息
typeid关键字返回对应参数的类型信息
一typeid返回一个type_info类对象
一当typeid的参数为NULL时将抛出异常
typeid 的注意事项
一当参数为类型时:返回静态类型信息
当参数为变量时:
·不存在虚函数表一返回静态类型信息
·存在虚函数表-返回动态类型信息
类型识别关键字
typeid关键字的使用
int i =0:
const type_info& tiv = typeid(i);
const type_info& tii = typeid(int);
cout << (tiv == tii)<< endl;
实验-typeid的使用
#include <iostream>
#include <string>
#include <typeinfo>//typeinfo头文件
using namespace std;
class Base//父类 基类
{
public:
virtual ~Base()
{}
};
class Derived : public Base//子类1
{
public:
void printf()
{
cout << "I'm a Derived." << endl;
}
};
void test(Base* b)
{
const type_info& tb=typeid(*b);
cout<<tb.name()<<endl;
}
int main(int argc, char *argv[])
{
int i=0;
const type_info& tiv = typeid(i);//静态类型信息
const type_info& tii = typeid(int);
cout << (tiv == tii)<< endl;
Base b;
Derived d;
test(&b);
test(&d);
return 0;
}
/*
父类没有虚函数:返回的是静态类型信息
1
4Base
4Base
父类有虚函数:返回动态类型信息
1
4Base
7Derived
*/
注意:不同的编译器使用typeid的返回值有差异
小结
C++中有静态类型和动态类型的概念
利用多态能够实现对象的动态类型识别!
typeid 是专用于类型识别的关键字
typeid能够返回对象的动态类型信息
第67课 - 经典问题解析五
指针的判别
■拾遗
-C++中仍然支持C语言中的可变参数函数
-C++编译器的匹配调用优先级
1.重载函数 2.函数模板 3.变参函数
面试问题
编写程序判断一个变量是不是指针。
指针的判别
·思路
一将变量分为两类:指针Vs非指针
一编写函数:
·指针变量调用时返回true
·非指针变量调用时返回false
实验-判断是否是指针
#include <iostream>
#include <string>
using namespace std;
//C++编译器的匹配调用优先级
//1.重载函数 2.函数模板 3.变参函数
class Test
{
public:
Test()
{
}
virtual ~Test()
{
}
};
template
<typename T>//模板匹配指针
bool IsPtr(T* v) // match pointer
{
return true;
}
bool IsPtr(...) // match non-pointer//变参函数不能识别class
{
return false;
}
int main(int argc, char *argv[])
{
int i = 0;
int* p = &i;
cout << "p is a pointer: " << IsPtr(p) << endl; // true
cout << "i is a pointer: " << IsPtr(i) << endl; // false
Test t;
Test* pt = &t;
cout << "pt is a pointer: " << IsPtr(pt) << endl; // true
//cout << "t is a pointer: " << IsPtr(t) << endl; // false
//codeblock报错,error: cannot pass objects of non-trivially-copyable type 'class Test' through '...'|
return 0;
}
实验-判断是否是指针 匹配函数不调用
//
#include <iostream>
#include <string>
using namespace std;
//C++编译器的匹配调用优先级
//1.重载函数 2.函数模板 3.变参函数
class Test
{
public:
Test()
{
}
virtual ~Test()
{
}
};
template
<typename T>//模板匹配指针
char IsPtr(T* v) // match pointer
{
return 'd';
}
int IsPtr(...) // match non-pointer//全局函数
{
return 0;
}
#define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char))
int main(int argc, char *argv[])
{
int i = 0;
int* p = &i;
cout << "p is a pointer: " << ISPTR(p) << endl; // true
cout << "i is a pointer: " << ISPTR(i) << endl; // false
Test t;
Test* pt = &t;
cout << "pt is a pointer: " << ISPTR(pt) << endl; // true
cout << "t is a pointer: " << ISPTR(t) << endl; // false
return 0;
}
指针的判别
存在的缺陷:
变参函数无法解析对象参数,可能造成程序崩溃!!
进一步的挑战:
如何让编译器精确匹配函数,但不进行实际的调用?
面试问题
如果构造函数中抛出异常会发生什么情况?
构造中的异常
■工程项目中的建议
不要在构造函数中抛出异常
一当构造函数可能产生异常时,使用二阶构造模式
构造中的异常
■构造函数中抛出异常
一构造过程立即停止
一当前对象无法生成
一析构函数不会被调用
一对象所占用的空间立即收回
实验-构造函数发生异常
#include <iostream>
#include <string>
using namespace std;
class Test
{
public:
Test()
{
cout << "Test()" << endl;
throw 0;
}
virtual ~Test()
{
cout << "~Test()" << endl;
}
};
int main(int argc, char *argv[])
{
Test* p = reinterpret_cast<Test*>(1);
//reinterpret_cast 强制类型转换
//一用于指针类型间的强制转换
//一用于整数和指针类型间的强制转换
try
{
cout << "p = new Test()..." << endl;
p = new Test();
cout << "p = new Test()...ok" << endl;
}
catch(...)
{
cout << "Exception..." << endl;
}
cout << "p = " << p << endl;
return 0;
}
/*
p = new Test()...
Test()
Exception...
p = 0x1 //初始化的值,p没有被赋值
*/
内存泄漏检测工具
g++ -g 67-2.cpp
valgrind --tool=memcheck --leak-check=full./a.out
析构中的异常
避免在析构函数中抛出异常!!
析构函数的异常将导致:
对象所使用的资源无法完全释放。
小结
C++中依然支持变参函数
变参函数无法很好的处理对象参数
利用函数模板和变参函数能够判断指针变量
构造函数和析构函数中不要抛出异常
第68课 - 拾遗:令人迷惑的写法
令人迷惑的写法
■typename诞生的直接诱因
一自定义类类型内部的嵌套类型
一不同类中的同一个标识符可能导致二义性
一编译器无法辨识标识符究竟是什么
令人迷惑的写法
历史上的原因。。
-早期的C++直接复用class关键字来定义模板
一但是泛型编程针对的不只是类类型
一class关键字的复用使得代码出现二义性
实验-函数模板 < class T >的作用
#include <iostream>
#include <string>
using namespace std;
template < class T >
class Test
{
public:
Test(T t)
{
cout << "t = " << t << endl;
}
};
template < class T >//函数模板template < class T > 等价于<typename T>
void func(T a[], int len)
{
for(int i=0; i<len; i++)
{
cout << a[i] << endl;
}
}
int main(int argc, char *argv[])
{
// test_class<Test_1>();
Test<string>ts("asdfdf");
string ta[]={"asf","gfd","fdg","fdggdf"};
func(ta,4);
/*t = asdfdf
asf
gfd
fdg
fdggdf */
Test<int>ti(100);
int ai[]={1,2,3,4};
func(ai,4);
/*
t = 100
1
2
3
4
*/
return 0;
}
令人迷惑的写法
typename的作用:
1.在模板定义中声明泛指类型
2.明确告诉编译器其后的标识符为类型
typename解决二义性问题
#include <iostream>
#include <string>
using namespace std;
int a = 0;//定义全局变量a
class Test_1
{
public:
static const int TS = 1;
};
class Test_2
{
public:
struct TS
{
int value;
};
};
template
< class T >//函数模板
void test_class()
{
//T::TS * a; //定义了一个指针; T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作 case1:
typename T::TS * a; // 1. 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (推荐的解读方式)
// 2. 使用泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作
//case2告诉编译器后面是类型
}
int main(int argc, char *argv[])
{
//case1;
// test_class<Test_1>();
//test_class<Test_2>();// error: dependent-name 'T::TS' is parsed as a non-type, but instantiation yields a type
//编译器默认乘法操作
//case2:
//test_class<Test_1>();//error: no type named 'TS' in 'class Test_1'
// test_class<Test_2>();
return 0;
}
令人迷惑的写法
■try…catch用于分隔正常功能代码与异常处理代码
■try…catch可以直接将函数实现分隔为2部分
■函数声明和定义时可以直接指定可能抛出的异常类型
■异常声明成为函数的一部分可以提高代码可读性
令人迷惑的写法
函数异常声明的注意事项
一函数异常声明是一种与编译器之间的契约
一函数声明异常后就只能抛出声明的异常
·抛出其它异常将导致程序运行终止
·可以直接通过异常声明定义无异常函数
实验-异常声明
#include <iostream>
#include <string>
using namespace std;
int func(int i,int j)throw(int,char)//异常声明 函数可能抛出异常
{
if((j>0)&&(j<10)){
return (i+j);
}
else
{
//throw 0;//case1
throw '0';//case2
}
}
void test(int i) try
{
cout<<"func(i,i)="<<func(i,i)<<endl;
}
catch(int i)
{
cout<<"Exception:"<<i<<endl;
}
catch(...)
{
cout<<"Exception...,i="<<i<<endl;
}
int main(int argc, char *argv[])
{
test(5);
test(10);
//case1
//func(1,1)=10
//Exception:0
//case2
//func(i,i)=10
//terminate called after throwing an instance of 'char'
//增加catch(...){cout<<"Exception..."<<i<<endl;},依然不行
//程序终止的原因:int func(int i,int j)throw(int)未改成int func(int i,int j)throw(char)
return 0;
}
小结
class可以用来在模板中定义泛指类型(不推荐)
typename是可以消除模板中的二义性
try…catch可以将函数体分成2部分
异常声明能够提供程序的可读性
第69课 - 技巧:自定义内存管理
笔试题
统计对象中某个成员变量的访问次数
遗失的关键字
■mutable的深入分析
mutable成员变量破坏了只读对象的内部状态
一const成员函数保证只读对象的状态不变性
mutable成员变量的出现无法保证状态不变性
遗失的关键字
■mutable是为了突破const函数的限制而设计的
mutable成员变量将永远处于可改变的状态
mutable在实际的项目开发中被严禁滥用
实验-统计对象中某个成员变量的访问次数:只读变量有问题
#include <iostream>
#include <string>
using namespace std;
//统计对象中某个成员变量的访问次数
class Test
{
int m_value;
int m_count;
public:
Test(int value = 0)
{
m_value = value;
m_count=0;
}
int getValue()const
{
m_count++;//error 2:
return m_value;
}
void setValue(int value)
{
m_count++;
m_value = value;
}
int getcount()const
{
return m_count;
}
};
int main(int argc, char *argv[])
{
Test t;
t.setValue(100);
//case1
cout << "t.m_value = " << t.getValue() << endl;//t.m_value = 0
cout << "t.m_count = " << t.getcount() << endl;
//只读对象
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;//error: passing 'const Test' as 'this' argument of 'int Test::getValue()' discards qualifiers [-fpermissive]|
cout << "ct.m_count = " << ct.getcount() << endl;
//getValue() 加上const
//只读变量的成员不可改变,依然报错
//E:\test\66-70\69\69-1.cpp|20|error: increment of member 'Test::m_count' in read-only object|
//遗失的关键字
//■mutable是为了突破const函数的限制而设计的
//mutable成员变量将永远处于可改变的状态
//mutable在实际的项目开发中被严禁滥用
//■mutable的深入分析
//mutable成员变量破坏了只读对象的内部状态
//一const成员函数保证只读对象的状态不变性
//mutable成员变量的出现无法保证状态不变性
return 0;
}
法一:mutable使用
#include <iostream>
#include <string>
using namespace std;
//统计对象中某个成员变量的访问次数
class Test
{
int m_value;
mutable int m_count;//最终使用mutable
public:
Test(int value = 0)
{
m_value = value;
m_count=0;
}
int getValue()const
{
m_count++;//error 2:
return m_value;
}
void setValue(int value)
{
m_count++;
m_value = value;
}
int getcount()const
{
return m_count;
}
};
int main(int argc, char *argv[])
{
Test t;
t.setValue(100);
//case1
cout << "t.m_value = " << t.getValue() << endl;//t.m_value = 0
cout << "t.m_count = " << t.getcount() << endl;
//只读对象
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;//error: passing 'const Test' as 'this' argument of 'int Test::getValue()' discards qualifiers [-fpermissive]|
cout << "ct.m_count = " << ct.getcount() << endl;
//getValue() 加上const
//只读变量的成员不可改变,依然报错
//E:\test\66-70\69\69-1.cpp|20|error: increment of member 'Test::m_count' in read-only object|
//遗失的关键字
//■mutable是为了突破const函数的限制而设计的
//mutable成员变量将永远处于可改变的状态
//mutable在实际的项目开发中被严禁滥用
//■mutable的深入分析
//mutable成员变量破坏了只读对象的内部状态
//一const成员函数保证只读对象的状态不变性
//mutable成员变量的出现无法保证状态不变性
return 0;
}
法二:利用指针
#include <iostream>
#include <string>
using namespace std;
//统计对象中某个成员变量的访问次数
class Test
{
int m_value;
int * const m_pcount;//另外的方法 指针的值不变,改变指向的值
//mutable int m_count;//最终使用mutable
public:
Test(int value = 0):m_pcount(new int(0))
{
m_value = value;
//m_count=0;
}
int getValue()const
{
// m_count++;//error 2:
*m_pcount=*m_pcount+1;
return m_value;
}
void setValue(int value)
{
// m_count++;
*m_pcount=*m_pcount+1;
m_value = value;
}
int getcount()const
{
/*return m_count;*/
return *m_pcount;
}
};
int main(int argc, char *argv[])
{
Test t;
t.setValue(100);
//case1
cout << "t.m_value = " << t.getValue() << endl;//t.m_value = 0
cout << "t.m_count = " << t.getcount() << endl;
//只读对象
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;//error: passing 'const Test' as 'this' argument of 'int Test::getValue()' discards qualifiers [-fpermissive]|
cout << "ct.m_count = " << ct.getcount() << endl;
return 0;
}
面试题
new关键字创建出来的对象位于什么地方?
被忽略的事实
■new/delete的本质是C++预定义的操作符
■C++对这两个操作符做了严格的行为定义
new :
1.获取足够大的内存空间(默认为堆空间)
2.在获取的空间中调用构造函数创建对象
delete:
1.调用析构函数销毁对象
2.归还对象所占用的空间(默认为堆空间)
new/delete的重载方式
// static member function
void* operator new (unsigned int size)
{
void* ret = NULL;
/* ret point to allocated memory */
return ret;
}
// static member function
void operator delete(void* p)
{
/* free the memory which is pointed by p */
}
被忽略的事实
■在C++中能够重载new/delete操作符
全局重载(不推荐)
局部重载(针对具体类进行重载)
重载new/delete的意义在于改变动态对象创建时的内存分配方式
实验
阶段一
#include <iostream>
#include <string>
using namespace std;
//重载new delete 在静态工作区分配空间
class Test
{
static const unsigned int COUNT=4;
static char c_buffer[];
static char c_map[];
int m_value;
public:
Test(int value = 0){
m_value = value;
}
void *operator new(unsigned int siz)
{
void * ret=NULL;
return ret;
}
void operator delete(void *p)
{
}
};
char Test::c_buffer[sizeof(Test)*Test::COUNT]={0};//分配4个Test对象大小的空间
char Test::c_map[Test::COUNT]={0};//标记使用的位置
int main(int argc, char *argv[])
{
Test *pt=new Test;
delete pt;
return 0;
}
实验:在静态工作区最多分配4个对象
#include <iostream>
#include <string>
using namespace std;
//重载new delete 在静态工作区分配空间
class Test
{
static const unsigned int COUNT=4;
static char c_buffer[];
static char c_map[];
int m_value;
public:
// Test(int value = 0){
// m_value = value;
//
// }
void *operator new(unsigned int siz)
{
void * ret=NULL;
for(unsigned int i=0;i<COUNT;i++)//若都被使用就返回空
{
if(!c_map[i])
{
c_map[i]=1;
ret=c_buffer+i*sizeof(Test);//参考1
cout<<"success to allocate memory:"<<ret<<endl;
break;
}
}
return ret;
}
void operator delete(void *p)
{
if(p!=NULL)
{
char*mem=reinterpret_cast<char *>(p);
unsigned int index=(mem-c_buffer)/sizeof(Test);//在标记数组的位置 参考1
int flag=(mem-c_buffer)%sizeof(Test);//flag!=0,不合法
if((flag==0)&&(index>=0)&&(index<COUNT))
{
c_map[index]=0;
cout<<"success to free memory:"<<p<<endl;
}
}
}
};
char Test::c_buffer[sizeof(Test)*Test::COUNT]={0};//分配4个Test对象大小的空间
char Test::c_map[Test::COUNT]={0};//标记使用的位置
int main(int argc, char *argv[])
{
Test *pt=new Test;
delete pt;
Test * pa[5]={0};
for(int i=0;i<5;i++)
{
pa[i]=new Test;
cout<<"pa["<<i<<"]"<<pa[i]<<endl;
}
for(int i=0;i<5;i++)
{
cout<<"before :delete:"<<pa[i]<<endl;
delete pa[i];
cout<<"after :delete:"<<pa[i]<<endl;
}
return 0;
}
设计思路
■解决方案
-在类中重载new/delete操作符
一在new的操作符重载函数中返回指定的地址
一在delete操作符重载中标记对应的地址可用
面试题
如何在指定的地址上创建C++对象?
实验
#include <iostream>
#include <string>
#include<cstdlib>
using namespace std;
//重载new delete 在静态工作区分配空间
//如何在指定的地址上创建C++对象?
class Test
{
static unsigned int c_count;//1no const
static char* c_buffer;//2array to pointer
static char* c_map;//3array to pointer
int m_value;
public:
// Test(int value = 0){
// m_value = value;
//
// }
static bool SetMemorySource(char* memory,unsigned int siz)//7
{
bool ret = false;
c_count=siz/sizeof(Test);//计算最多可以创建多少个对象
ret=(c_count&&(c_map=reinterpret_cast<char*>(calloc(c_count,sizeof(char)))));
if(!ret)
{
c_buffer=memory;
}
else
{
free(c_map);
c_map=NULL;
c_count=0;
}
return ret;
}
void *operator new(unsigned int siz)
{
void * ret=NULL;
if(c_count>0)
{
for(unsigned int i=0;i<c_count;i++)//若都被使用就返回空
{
if(!c_map[i])
{
c_map[i]=1;
ret=c_buffer+i*sizeof(Test);//参考1
cout<<"success to allocate memory:"<<ret<<endl;
break;
}
}
}
else
{
ret=malloc(siz);
cout<<"success to malloc:"<<ret<<endl;
}
return ret;
}
void operator delete(void *p)
{
if(p!=NULL)
{
if(c_count>0)
{
char*mem=reinterpret_cast<char *>(p);
unsigned int index=(mem-c_buffer)/sizeof(Test);//在标记数组的位置 参考1
int flag=(mem-c_buffer)%sizeof(Test);//flag!=0,不合法
if((flag==0)&&(index>=0)&&(index<c_count))
{
c_map[index]=0;
cout<<"success to free memory:"<<p<<endl;
}
}
else
{
free(p);
}
}
}
};
unsigned int Test::c_count=0;//6初始化变量
char* Test::c_buffer=NULL;//4
char * Test::c_map=NULL;//5
int main(int argc, char *argv[])
{
Test *pt=new Test;
delete pt;
cout<<"sizeof(Test):"<<sizeof(Test)<<endl;
Test * pa[5]={0};
for(int i=0;i<5;i++)
{
pa[i]=new Test;
cout<<"pa["<<i<<"]"<<pa[i]<<endl;
}
for(int i=0;i<5;i++)
{
cout<<"before :delete:"<<pa[i]<<endl;
delete pa[i];
cout<<"after :delete:"<<pa[i]<<endl;
}
return 0;
}
success to malloc:0xa61600
sizeof(Test):4
success to malloc:0xa61600
pa[0]0xa61600
success to malloc:0xa61610
pa[1]0xa61610
success to malloc:0xa61620
pa[2]0xa61620
success to malloc:0xa61630
pa[3]0xa61630
success to malloc:0xa61640
pa[4]0xa61640
before :delete:0xa61600
after :delete:0xa61600
before :delete:0xa61610
after :delete:0xa61610
before :delete:0xa61620
after :delete:0xa61620
before :delete:0xa61630
after :delete:0xa61630
before :delete:0xa61640
after :delete:0xa61640
分配到栈上去
#include <iostream>
#include <string>
#include<cstdlib>//
using namespace std;
//重载new delete 在静态工作区分配空间
//如何在指定的地址上创建C++对象?
class Test
{
static unsigned int c_count;//1no const
static char* c_buffer;//2array to pointer
static char* c_map;//3array to pointer
int m_value;
public:
// Test(int value = 0){
// m_value = value;
//
// }
static bool SetMemorySource(char* memory,unsigned int siz)//7
{
bool ret = false;
c_count=siz/sizeof(Test);//计算最多可以创建多少个对象
ret=(c_count&&(c_map=reinterpret_cast<char*>(calloc(c_count,sizeof(char)))));
if(ret)//if(!ret)
{
c_buffer=memory;
}
else
{
free(c_map);
c_map=NULL;
c_count=0;
}
return ret;
}
void *operator new(unsigned int siz)
{
void * ret=NULL;
if(c_count>0)
{
for(unsigned int i=0;i<c_count;i++)//若都被使用就返回空
{
if(!c_map[i])
{
c_map[i]=1;
ret=c_buffer+i*sizeof(Test);//参考1
cout<<"success to allocate memory:"<<ret<<endl;
break;
}
}
}
else
{
ret=malloc(siz);
cout<<"success to malloc:"<<ret<<endl;
}
return ret;
}
void operator delete(void *p)
{
if(p!=NULL)
{
if(c_count>0)
{
char*mem=reinterpret_cast<char *>(p);
unsigned int index=(mem-c_buffer)/sizeof(Test);//在标记数组的位置 参考1
int flag=(mem-c_buffer)%sizeof(Test);//flag!=0,不合法
if((flag==0)&&(index>=0)&&(index<c_count))
{
c_map[index]=0;
cout<<"success to free memory:"<<p<<endl;
}
}
else
{
free(p);
}
}
}
};
unsigned int Test::c_count=0;//6初始化变量
char* Test::c_buffer=NULL;//4
char * Test::c_map=NULL;//5
int main(int argc, char *argv[])
{
char buffer[12]={0};
bool re=Test::SetMemorySource(buffer,sizeof(buffer));
cout<<"Test::SetMemorySource(buffer,sizeof(buffer))"<<re<<endl;
cout<<"====single===="<<endl;
Test *pt=new Test;
delete pt;
cout<<"====Array===="<<endl;
cout<<"sizeof(Test):"<<sizeof(Test)<<endl;
Test * pa[5]={0};
for(int i=0;i<5;i++)
{
pa[i]=new Test;
cout<<"pa["<<i<<"]"<<pa[i]<<endl;
}
for(int i=0;i<5;i++)
{
cout<<"delete:"<<pa[i]<<endl;
delete pa[i];
//cout<<"after :delete:"<<pa[i]<<endl;
}
return 0;
}
//Test::SetMemorySource(buffer,sizeof(buffer))1
//====single====
//success to allocate memory:0x69fec4
//success to free memory:0x69fec4
//====Array====
//sizeof(Test):4
//success to allocate memory:0x69fec4
//pa[0]0x69fec4
//success to allocate memory:0x69fec8
//pa[1]0x69fec8
//success to allocate memory:0x69fecc
//pa[2]0x69fecc
//pa[3]0
//pa[4]0
//delete:0x69fec4
//success to free memory:0x69fec4
//delete:0x69fec8
//success to free memory:0x69fec8
//delete:0x69fecc
//success to free memory:0x69fecc
//delete:0
//delete:0
被忽略的事实
■注意事项
new[]实际需要返回的内存空间可能比期望的要多
一对象数组占用的内存中需要保存数组信息
一数组信息用于确定构造函数和析构函数的调用次数
被忽略的事实
■new/delete的重载方式
// static memberfunction
voidt operator new[](unsigned int size)
{
return malloc(size);
}
//static membe rfunction
void operator delete[] (void* p)
{
free (p) ;
}
被忽略的事实
new/delete与new/delete完全不同动态
对象数组创建通过newl完成
动态对象数组的销毁通过delete[]完成
new[]/delete[]能够被重载,进而改变内存管理方式
实验-new[]/delete[]&new/delete
#include <iostream>
#include <string>
#include<cstdlib>//
using namespace std;
//动态数组的创建
class Test
{
int m_value;
public:
Test()
{
m_value=0;
}
~Test()
{
}
void *operator new(unsigned int siz)编译器自动赋值 siz
{
cout<<"operator new:"<<siz<<endl;
return malloc(siz);
}
void operator delete(void *p)
{
cout<<"operator delete:"<<p<<endl;
free(p);
}
void *operator new[](unsigned int siz)//
{
cout<<"operator new[]:"<<siz<<endl;
return malloc(siz);
}
void operator delete[](void *p)
{
cout<<"operator delete[]:"<<p<<endl;
free(p);
}
};
int main(int argc, char *argv[])
{
Test * pt=NULL;
cout<<"sizeof(Test):"<<sizeof(Test)<<endl;
pt=new Test;//siz=sizeof(Test) malloc(siz)====>malloc(sizeof(Test));
delete pt;
pt=new Test[5];siz=sizeof(Test)*5+sizeof(5)
delete[] pt;
return 0;
}
//operator new:4
//operator delete:0x121600
//operator new[]:24 //数组的大小信息 20+4
//operator delete[]:0x121600
//必须成对使用
小结
new/delete的本质为操作符
可以通过全局函数重载new/delete(不推荐)
可以针对具体的类重载new/delete
new[]/delete[]与new/delete完全不同
new[]/delete[]也是可以被重载的操作符
new[]返回的内存空间可能比期望的要多
第70课 - 展望:未来的学习之路
未来的学习之路
本课程学习的是C++98/03标准
一C++98/03标准在实际工程中的常用特性
大多数企业的产品开发中需要使用的C++技能
课程总结
■C++语言的学习需要重点在于以下几个方面
一C语言到C++的改进有哪些?
一面向对象的核心是什么?
一操作符重载的本质是什么?
一模板的核心意义是什么?
一异常处理的使用方式是什么?