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++的改进有哪些?
一面向对象的核心是什么?
一操作符重载的本质是什么?
一模板的核心意义是什么?
一异常处理的使用方式是什么?

  • 24
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值