C++中重载定位new表达式

103 篇文章 4 订阅
4 篇文章 0 订阅
  1. new表达式

当我们使用一条 new表达式时:

string *sp = new string("hello, world");
string *arr = new string[10];

实际执行了三步操作:
第一步,new表达式调用一个名为 operator new(或者 operator new[])的标准库函数,该函数分配一块足够大的内存空间以便存储特定类型的对象(或者对象的数组);
第二步,编译器运行相应的构造函数以构造这些对象,并为其传入初始值;
第三步,对象被构造完成,返回一个指向该对象的指针。

  1. 重载 operator new()函数

如果应用程序希望控制内存分配的过程中,比如从一个内存池中分配内存,而不是通过系统调用来分配内存,则可以重载 operator new()函数。其中,operator new()的第一个参数(可能有多个参数,后面再讲)是size_t,由new表达式负责传入具体的值。下面是在一个类中重载operator new()函数的示例:

#include <iostream>
#include <string>
#include <cstdlib>

class Foo
{
public:
	Foo(const char *ptr)
	{
		std::cout << "Foo()" << std::endl;
		str = ptr;
	}
	void *operator new(size_t size)
	{
		std::cout << "Foo::operator new(): input_size = " << size << std::endl;
		std::cout << "sizeof Foo: " << sizeof(Foo) << std::endl;

		void *ret = malloc(size);
		std::cout << "malloced addr: " << ret << std::endl;

		return ret;
	}
	void show(void)
	{
		std::cout << str << std::endl;
	}
private:
	std::string str;
};

int main(void)
{
	Foo *f = new Foo("hello, world");
	std::cout << "object addr: " << f << std::endl;

	f->show();

	return 0;
}

程序运行结果如下所示:

$ g++ -Wall -lpthread -std=c++11 main.cpp -o main -O3
$ ./main                
Foo::operator new(): input_size = 24
sizeof Foo: 24
malloced addr: 0x7fdea8400640
Foo()
object addr: 0x7fdea8400640
hello, world
  1. 定位 new基本用法

定位 new也即是 placement new,它的基本用法是:

new (placement-args ) type new-initializer

其中,placement-args是复数形式,也就是说可以有多个参数,这些参数会被放到operator new()的第一个参数后面。

具有特定参数列表的operator new()需要由用户显式定义,示例如下所示:

#include <iostream>
#include <cstdlib>

void *operator new(size_t size, size_t ext1, size_t ext2)
{
	std::cout << size << ", " << ext1 << ", " << ext2 << std::endl;
	return malloc(size + ext1 + ext2);
}

struct node {
	int val;
	struct node *next;
	int arr[0];
};

int main(void)
{
	struct node *n = (struct node *)new (4, 8) int;
	n->val = 1;
	n->next = NULL;
	n->arr[0] = 1;

	return 0;
}

程序运行结果如下所示:

$ g++ -Wall -lpthread -std=c++11 main.cpp -o main -O3
$ ./main                
operator new(): 4, 4, 8
  1. 定位 new的特殊形式

定位new有一种特殊形式,应用程序不能重载:

new (void* ) type new-initializer

也就是说,当 placement-args是一个 指针类型 的形参时,不能自定义对应的 operator new()函数,因为系统库已经定义了 void *operator new(size_t, void*)函数,不允许重载,否则将会报错。

下面的代码尝试重载该函数:

#include <iostream>

void *operator new(size_t size, void *p) 
{
    return malloc(size);
}

int main(void)
{
    int a = 2024;
    int *b = new (&a) int;
    std::cout << *b << std::endl;

    return 0;
}

程序编译结果如下所示:

$ g++ -Wall -lpthread -std=c++11 main.cpp -o main -O3
main.cpp:3:7: error: 'operator new' is missing exception specification 'noexcept'
void *operator new(size_t size, void *p)
      ^
                                         noexcept
/Library/Developer/CommandLineTools/usr/include/c++/v1/new:219:70: note: previous declaration is here
_LIBCPP_NODISCARD_AFTER_CXX17 inline _LIBCPP_INLINE_VISIBILITY void* operator new  (std::size_t, void* __p) _NOEXCEPT {ret...
                                                                     ^
  1. 定位 new特殊形式的用法

上面说的void *operator new(size_t, void*)函数有特殊的用途,该函数不分配任何内存,它只是简单地返回 指针实参;然后由 new表达式负责在指定的地址上初始化对象以完成整个工作。事实上,上述形式的定位 new允许我们在一个特定的、预先分配的内存地址上构造对象。下面是上述定位 new的一个使用示例:

#include <iostream>

class Foo
{
public:
	Foo(int num)
	{
		std::cout << "Foo()" << std::endl;
		val_ = num;
	}
	int val_;
};

int main(void)
{
	void *addr = malloc(sizeof(Foo));
	std::cout << addr << std::endl;

	Foo *f = new (addr) Foo(2024);
	std::cout << f << std::endl;
	std::cout << f->val_ << std::endl;	

	return 0;
}

程序运行结果如下所示:

$ g++ -Wall -lpthread -std=c++11 main.cpp -o main -O3
$ ./main                
0x7fea294006a0
Foo()
0x7fea294006a0
2024

参考资料:

  1. C++ primer(第五版)中文版。
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值