文章目录

  • ​​1.类型转换运算符​​
  • ​​2.->运算符​​
  • ​​3.operator new、operator delete,new运算符的3种用法​​

1.类型转换运算符

  • 必须是成员函数,不能是友元函数
  • 没有参数(操作数是什么?)
    因为操作数就是类对象自身
  • 不能指定返回类型(其实已经指定了)
    它要指定的类型就是返回类型
  • 函数原型:
operator 类型名();
  • eg:25cpp\25cpp\25cpp\01.cpp
#include "Integer.h"
#include <iostream>
using namespace std;

int add(int a, int b)
{
return a + b;
}

int main(void)
{
Integer n(100);
n = 200;//通过转换构造函数Integer(int n);也叫构造函数,可以将200转换为类类型,赋值给n
n.Display();

int sum = add(n, 100);

cout<<sum<<endl;

int x = n;//隐式转换:调用类型转换运算符operator int();
int y = static_cast<int>(n);//显示转换,调用类型转换运算符operator int();

return 0;
}
  • 类声明及定义
    25cpp\25cpp\25cpp\Integer.h
#ifndef _INTEGER_H_
#define _INTEGER_H_

class Integer
{
public:
Integer(int n);
~Integer();

Integer& operator++();
//friend Integer& operator++(Integer& i);

Integer operator++(int n);
//friend Integer operator++(Integer& i, int n);

operator int();//没有参数,就是将自身转换为int型

void Display() const;
private:
int n_;
};

#endif // _INTEGER_H_

25cpp\25cpp\25cpp\Integer.cpp

#include "Integer.h"
#include <iostream>
using namespace std;

Integer::Integer(int n) : n_(n)
{
}

Integer::~Integer()
{
}

Integer& Integer::operator ++()
{
//cout<<"Integer& Integer::operator ++()"<<endl;
++n_;
return *this;
}

//Integer& operator++(Integer& i)
//{
// //cout<<"Integer& operator++(Integer& i)"<<endl;
// ++i.n_;
// return i;
//}

Integer Integer::operator++(int n)
{
//cout<<"Integer& Integer::operator ++()"<<endl;
//n_++;
Integer tmp(n_);
n_++;
return tmp;
}

//Integer operator++(Integer& i, int n)
//{
// Integer tmp(i.n_);
// i.n_++;
// return tmp;
//}

Integer::operator int()
{
return n_;
}

void Integer::Display() const
{
cout<<n_<<endl;
}
  • 测试:

2.->运算符

  • eg:25cpp\25cpp\25cpp\02.cpp
#include <iostream>
using namespace std;

class DBHelper
{
public:
DBHelper()
{
cout<<"DB ..."<<endl;
}
~DBHelper()
{
cout<<"~DB ..."<<endl;
}

//打开数据库
void Open()
{
cout<<"Open ..."<<endl;
}

//关闭数据库
void Close()
{
cout<<"Close ..."<<endl;
}

//查询数据库
void Query()
{
cout<<"Query ..."<<endl;
}
};

class DB
{
public:
DB()
{
//构造一个DBHelper对象
db_ = new DBHelper;
}

~DB()
{
delete db_;
}

//重载指针运算符->
//指针运算符重载返回一个内部的指针,然后通过内部的指针访问DBHelper的成员函数
DBHelper* operator->()
{
return db_;
}
private:
DBHelper* db_;//DBHelper对象,函数指针是db_
};


int main(void)
{
//利用类对象db确定析构机制,将它所包装的动态对象销毁掉
DB db;
db->Open();
db->Query();
db->Close();


return 0;
}
  • 测试:
    指针运算符重载

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_Test

3.operator new、operator delete,new运算符的3种用法

  • (1)operator new与之对应的delete操作是:
void* operator new(size_t size)

void operator delete(void* p)
New运算符重载,delete运算符也要被重载
void operator delete(void* p, size_t size)
与void operator delete(void* p)可以共存,先调用void operator delete(void* p)
  • (2)这个operator new与之对应的delete操作是:
void* operator new(size_t size, const char* file, long line)

void operator delete(void* p, const char* file, long line)
void operator delete(void* p, size_t size, const char* file, long line)
两者是等价的,都出现时,上面的delete优先
  • (3)针对数组而言,这个operator new与之对应的delete操作是:
void* operator new[](size_t size)

void operator delete[](void* p)
void operator delete[](void* p, size_t size)
  • new有3种用法
    (1)new operator:不能被重载
    (2)operator new:可以被重载
    (3)placement new
    placement new是在已经申请的内存上构建对象. 这就是我们调用new的时候会调用对象的构造函数的原因。
  • eg:25cpp\25cpp\25cpp\03.cpp
#include <iostream>
using namespace std;



class Test
{
public:
Test(int n) : n_(n)
{
cout<<"Test(int n) : n_(n)"<<endl;
}
//拷贝构造函数
Test(const Test& other)
{
cout<<"Test(const Test& other)"<<endl;
}
//析构函数不能被重载,只能有一个
~Test()
{
cout<<"~Test()"<<endl;
}

void* operator new(size_t size)
{
cout<<"void* operator new(size_t size)"<<endl;
void* p = malloc(size);
return p;
}

void operator delete(void* p)
{
cout<<"void operator delete(void* p)"<<endl;
free(p);
}

void operator delete(void* p, size_t size)
{
cout<<"void operator delete(void* p, size_t size)"<<endl;
free(p);
}

void* operator new(size_t size, const char* file, long line)
{
//打印哪一个文件,哪一行
cout<<file<<":"<<line<<endl;
void* p = malloc(size);
return p;
}

void operator delete(void* p, const char* file, long line)
{
//打印哪一个文件,哪一行
cout<<file<<":"<<line<<endl;
free(p);
}

void operator delete(void* p, size_t size, const char* file, long line)
{
cout<<file<<":"<<line<<endl;
free(p);
}

//placement new的用法如下:
//这是局部的 placement new,已经存在的内存地址,直接返回就可以了,内部不需要分配空间
//所以相应的delete不需要做任何的操作
void* operator new(size_t size, void* p)
{
return p;
}

void operator delete(void *, void *)
{
}

int n_;
};

void* operator new(size_t size)
{
cout<<"global void* operator new(size_t size)"<<endl;
void* p = malloc(size);
return p;
}

void operator delete(void* p)
{
cout<<"global void operator delete(void* p)"<<endl;
free(p);
}

void* operator new[](size_t size)
{
cout<<"global void* operator new[](size_t size)"<<endl;
void* p = malloc(size);
return p;
}

void operator delete[](void* p)
{
cout<<"global void operator delete[](void* p)"<<endl;
free(p);
}

int main(void)
{
//new operator用法
Test* p1 = new Test(100); // 称作是:new operator,new operator = operator new内存分配 + 构造函数的调用
delete p1;

//这个是全局的operator new操作
char* str = new char[100];
delete[] str;

char chunk[10];

//placement new用法
//在chunk这块内存上进行new操作
//返回的是p2指针就等于chunk的地址
//p2的地址就等于chunk地址,4个字节的Test对象使用的空间就是chunk空间的4个字节
Test* p2 = new (chunk) Test(200); //operator new(size_t, void *_Where)
// placement new,不分配内存 + 构造函数的调用
cout<<p2->n_<<endl;
p2->~Test(); // 显式调用析构函数,析构函数可以被显式调用的
//不能使用delete p2,因为它不是堆上生成的内存,它的内存在已经存在的chunk内存中
//所以要使用显式调用析构函数的方式来调用

//强制转换
//Test* p3 = (Test*)chunk;等价于下面的类型转换
Test* p3 = reinterpret_cast<Test*>(chunk);
cout<<p3->n_<<endl;



//Test* p4 = new(__FILE__, __LINE__) Test(300);//传递的是operator new+构造函数的调用,operator不仅
//传递了对象的大小300,还传递了__FILE__, __LINE__
//delete(__FILE__, __LINE__) p4;delete p4(__FILE__, __LINE__);都没有调用与上面想对应的delete函数,后期有空可以研究下

//__FILE__表示这行所在的文件
//__LINE__表示这行所在的行号
#define new new(__FILE__, __LINE__)
Test* p4 = new Test(300);
delete p4;

return 0;
}
  • 测试1:
Test* p1 = new Test(100); // 称作是:new operator,new operator = operator new内存分配 + 构造函数的调用

利用VS2008,跟踪一下,按下F9,

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_运算符_02


按下F11,调用operator new,传递了一个大小进来,当前类的大小是4个字节

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_构造函数_03


按下shift F11可以跳出这个函数,或者按下F10不去跟踪这一行函数

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_Test_04


(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_运算符_05

  • 测试2:
char chunk[10];
//placement new用法
//在chunk这块内存上进行new操作
//返回的是p2指针就等于chunk的地址
//p2的地址就等于chunk地址,4个字节的Test对象使用的空间就是chunk空间的4个字节
Test* p2 = new (chunk) Test(200); //operator new(size_t, void *_Where)
// placement new,不分配内存 + 构造函数的调用
cout<<p2->n_<<endl;
//强制转换
//Test* p3 = (Test*)chunk;
Test* p3 = reinterpret_cast<Test*>(chunk);
cout<<p3->n_<<endl;

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_运算符_06


F11,直接返回Where指针,Where的地址就是chunk的地址

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_Test_07


再次按下F10

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_构造函数_08


返回了一个存在的指针,构造的对象放在了chunk地址里面了

chunk地址与p2地址是一样的

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_Test_09

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_Test_10

  • 测试3:测试的上面的(1)operate new的用法

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_构造函数_11

  • 测试4:
//这个是全局的operator new操作
char* str = new char[100];
delete[] str;

重载的是带数组的operator new:void* operator new[](size_t size)

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_运算符_12

  • 测试5:测试的是上面(2)operate new的用法
void* operator new(size_t size)
{
cout<<"void* operator new(size_t size)"<<endl;
void* p = malloc(size);
return p;
}
。。。。
。。。。
。。。。
void operator delete(void *, void *)
{
}
将上述重载的new与delete的运算符全都屏蔽掉,进入debug,看到了应该与operator new相匹配的operator delete的原型

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_构造函数_13

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_运算符_14

//Test* p4 = new(__FILE__, __LINE__) Test(300);//传递的是operator new+构造函数的调用,operator不仅
//传递了对象的大小300,还传递了__FILE__, __LINE__
//delete(__FILE__, __LINE__) p4;delete p4(__FILE__, __LINE__);都没有调用与上面想对应的delete函数,后期有空可以研究下

//__FILE__表示这行所在的文件
//__LINE__表示这行所在的行号
#define new new(__FILE__, __LINE__)
Test* p4 = new Test(300);
delete p4;

(P25)运算符重载:类型转换运算符 ,->运算符 ,operator new、operator delete,new运算符的3种用法_Test_15

  • 参考:​​new实现​​