ページ 11

std::listでnew・deleteをオーバーロード

Posted: 2016年3月02日(水) 19:28
by 夢幻ノ月夜
基底クラスとなるTaskクラスを定義し、そこから敵やら弾やらのクラスを派生する形で作っているのですが、
std::listに項目を追加・削除する作業をnew・deleteのオーバーロードで行いたいです。
(Taskの派生クラスの中ではnewとdeleteをタスクリストに追加削除すると定義したい)
deleteの中では普通に削除するだけですが、newには描画優先度も引数として持たせたいです
ソートは<をオーバーロードして実装するつもりですが、
newとdeleteでlistに追加・削除するにはどうしたらいいでしょうか
どうやらnewはsize_tを受け取ってvoid*を返しているようなのですが…
具体的に何をやっているのかよく分かりません
なお目的は「delete this;」で自分自身を削除出来ることです

↓現時点でのコード

コード:

#include <list>

using namespace std;

class Task{
protected:
	int m_id;
	float priority;
public:
	void* operator new(size_t size,float priority=0.5f);
	void operator delete(void* pTask);
};

list<Task*> TaskList;

Re: std::listでnew・deleteをオーバーロード

Posted: 2016年3月03日(木) 10:18
by tk-xleader
 operator newは、第一引数にnew演算子の型のバイトサイズが渡されます。そして、少なくとも第一引数のバイト数は書き込み可能なメモリブロックを戻り値とします。他方、operator deleteは、delete演算子のオペランドのメモリブロックがそのまま渡されます。そのメモリブロックは既に解体済み(デストラクタが呼ばれた後)なので、通常はメモリブロックの解放処理を記述します。

 原則として、operator new/deleteがやるべきことはメモリブロックの確保/解放(placement newの場合は受け取ったメモリブロックをそのまま返すだけですが)なので、そこでTask型の存在を前提とした処理を行うということは普通はしないのではないかと思います。通常ならそれはコンストラクタ/デストラクタの仕事ではないかと。

Re: std::listでnew・deleteをオーバーロード

Posted: 2016年3月03日(木) 10:40
by YuO
あくまでoperator new,operator deleteのオーバーロードであって,new operatorやdelete operatorのオーバーロードではありません。
また,new operator/delete operatorはオーバーロードできません。
つまり,malloc/free相当部分は変更が出来るものの,コンストラクタの呼び出しやその引数は変更できません。
このため,operator newの引数をオブジェクトに引き渡すには,別の領域に保持しておいて,そこから値を取得するしかありません。

なお,operator newには対応するoperator deleteが原則的に必要です。
void * operator new (std::size_t, float)に対応するのは,void operator delete (void *, float)またはvoid operator delete (void *, std::size_t, float)です。
コンストラクタで例外を発生させた場合などに,こちらのoperator deleteが呼ばれます。
# deleteした場合は通常のoperator deleteが呼ばれるので注意。

Re: std::listでnew・deleteをオーバーロード

Posted: 2016年3月03日(木) 13:50
by sleep
new、deleteについて
夢幻ノ月夜 さんが書きました: どうやらnewはsize_tを受け取ってvoid*を返しているようなのですが…
具体的に何をやっているのかよく分かりません
この辺のnew、deleteに関する知識は、STLで使用されているallocatorを自作してみると理解できます。
重要なのは4つの関数で行われている動作です。
allocate、deallocate、construct、destroy
これらが「いつ呼び出されて」「何をしているのか」が理解できれば、new、deleteの動作に関する疑問は解消されることでしょう。

Windows: VS 2015
Linux: gcc 5.2.1

コード:

//g++ -std=c++14 -o test test.cpp
#include <iostream>
#include <string>
#include <memory>
#include <new>
#include <cstddef>
#include <stdexcept>

template <class T>
class custom_allocator
{
public:
	static_assert(!std::is_const<T>::value, "custom_allocator<const T> is ill-formed.");

	using value_type = T;
	using pointer = value_type*;
	using const_pointer = const pointer;
	using reference = value_type&;
	using const_reference = const value_type&;
	using size_type = std::size_t;
	using difference_type = std::ptrdiff_t;

	template <class Other>
	struct rebind { using other = custom_allocator<Other>; };
	pointer address(reference value) const { return &value; }
	const_pointer address(const_reference value) const { return &value; }
	constexpr size_type max_size() const noexcept { return static_cast<size_type>(-1) / sizeof(T); }

	template <class Other>
	custom_allocator<value_type>& operator = (const custom_allocator<Other>&) const { return *this; }
	inline bool operator == (const custom_allocator&) const { return true; }
	inline bool operator != (const custom_allocator&) const { return false; }

	custom_allocator() noexcept {}
	custom_allocator(const custom_allocator&) noexcept {}
	template <class Other>
	custom_allocator(const custom_allocator<Other>&) noexcept {}

	pointer allocate(size_type count, const void* = 0) const throw(std::bad_alloc, std::length_error)
	{
		if (count == 0) return nullptr;
		if (count > max_size()) throw std::length_error("custom_allocator<value_type>::allocate(size_type count, const void* = 0)  ->   count > max_size()");

		void* const p = ::operator new (sizeof(value_type) * count, std::nothrow);
		if (p == nullptr) throw std::bad_alloc();

		std::cout << "allocate   : " << std::hex << p << std::dec << " <- " << sizeof(value_type) * count << " byte  [" << typeid(value_type).name() << " : " << sizeof(value_type) << " byte] * " << count << std::endl;

		return static_cast<pointer>(p);
	}
	void deallocate(pointer p, size_type count) const
	{
		if (p == nullptr) return;
		::operator delete((void*)p);

		std::cout << "deallocate : " << std::hex << p << std::dec << " -> " << sizeof(value_type) * count << " byte  [" << typeid(value_type).name() << " : " << sizeof(value_type) << " byte] * " << count << std::endl;
	}

	template <class ObjType, class... Args>
	void construct(ObjType *p, Args&&... args) const
	{
		std::cout << "construct  : " << typeid(ObjType).name() << std::endl;

		::new((void*)p) ObjType(std::forward<Args>(args)...);
	}
	template <class ObjType>
	void destroy(ObjType *p) const
	{
		std::cout << "destroy    : " << typeid(ObjType).name() << std::endl;

		p->~ObjType();
	}
};

#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <list>
#include <unordered_map>
#include <iterator>
using namespace std;

class A
{
public:
	int a = 0;
	~A()
	{
		cout << "デストラクタ" << endl;
	}
};

int main()
{
#ifdef _WIN32
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
#endif

	{
		cout << "std::vector" << endl;
		cout << "-----------------------------------" << endl;
		auto v = std::vector<A, custom_allocator<A>>();
		//v.reserve(10);
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------";
		cin.ignore();
	}
	cout << "-----------------------------------" << endl;
	cin.ignore();
	{
		cout << "std::deque" << endl;
		cout << "-----------------------------------" << endl;
		auto v = std::deque<A, custom_allocator<A>>();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------";
		cin.ignore();
	}
	cout << "-----------------------------------" << endl;
	cin.ignore();
	{
		cout << "std::stack (vector)" << endl;
		cout << "-----------------------------------" << endl;
		auto v = std::stack<A, vector<A, custom_allocator<A>>>();
		cout << "-----------------------------------" << endl;
		v.emplace();
		cout << "-----------------------------------" << endl;
		v.emplace();
		cout << "-----------------------------------" << endl;
		v.emplace();
		cout << "-----------------------------------";
		cin.ignore();
	}
	cout << "-----------------------------------" << endl;
	cin.ignore();
	{
		cout << "std::stack (deque)" << endl;
		cout << "-----------------------------------" << endl;
		auto v = std::stack<A, deque<A, custom_allocator<A>>>();
		cout << "-----------------------------------" << endl;
		v.emplace();
		cout << "-----------------------------------" << endl;
		v.emplace();
		cout << "-----------------------------------" << endl;
		v.emplace();
		cout << "-----------------------------------";
		cin.ignore();
	}
	cout << "-----------------------------------" << endl;
	cin.ignore();
	{
		cout << "std::list" << endl;
		cout << "-----------------------------------" << endl;
		auto v = std::list<A, custom_allocator<A>>();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------" << endl;
		v.emplace_back();
		cout << "-----------------------------------";
		cin.ignore();
	}
	cout << "-----------------------------------" << endl;
	cin.ignore();
	{
		cout << "std::unordered_map" << endl;
		cout << "-----------------------------------" << endl;
		auto v = std::unordered_map<string, string, std::hash<string>, std::equal_to<string>, custom_allocator<std::pair<const string, string>>>();
		cout << "-----------------------------------" << endl;
		v["A"] = "hello";
		cout << "-----------------------------------" << endl;
		v["B"] = "byte";
		cout << "-----------------------------------" << endl;
		v["C"] = "good";
		cout << "-----------------------------------" << endl;
		cout << R"(v["A"] = )" << v["A"] << endl;
		cout << R"(v["B"] = )" << v["B"] << endl;
		cout << R"(v["C"] = )" << v["C"] << endl;
		cout << "-----------------------------------";
		cin.ignore();
	}
	cout << "-----------------------------------" << endl;
	cin.ignore();
}

Re: std::listでnew・deleteをオーバーロード

Posted: 2016年3月03日(木) 16:29
by 夢幻ノ月夜
次のゲーム作るときにリスト管理のプログラムを先に作ります(´・ω・`)
今のゲームは今の管理機構で完成させたいと思います(´・ω・`)
ありがとうございました(´・ω・`)

Re: std::listでnew・deleteをオーバーロード

Posted: 2016年3月03日(木) 21:05
by いわん
newとdeleteの仕組みについて、興味があったので自分なりにコードを書いてみました。
一応動いているみたいですが、まだよくわかっていないこともあり問題あるコードだと思いますので詳しい方のチェックお願いします。
エラーチェック、例外発生時の対応等一切していません^^;

コード:

// compiler : Microsoft Visual C++ 2015
// project template : Win32 コンソールアプリケーション

#include "stdafx.h"

#include <list>
using namespace std;

class Task
{
protected:
	static list<Task*> TaskList;
	int m_id;
	float priority;
public:
	static void* operator new(size_t size, int id, float priority = 0.5f)
	{
		printf("new id=%d\n", id);
		Task *pTask = (Task*)malloc(size);
		pTask->m_id = id;
		pTask->priority = priority;
		TaskList.push_back(pTask);
		return pTask;
	}
	static void operator delete(void* pTask)
	{
		list<Task*>::iterator ite;
		for (ite = TaskList.begin(); ite != TaskList.end(); ++ite)
		{
			if (*ite == pTask)
			{
				printf("delete id=%d\n", (*ite)->m_id);
				TaskList.erase(ite);
				free(pTask);
				break;
			}
		}
	}
	static void PrintList()
	{
		printf("TaskList size = %d\n", TaskList.size());
		list<Task*>::iterator ite;
		for (ite = TaskList.begin(); ite != TaskList.end(); ++ite)
		{
			printf("m_id=%d priority=%g\n", ((Task*)*ite)->m_id, ((Task*)*ite)->priority);
		}
	}
};
list<Task*> Task::TaskList;

int main()
{
	Task *ptask1, *ptask2, *ptask3;
	ptask1 = new(1) Task;
	ptask2 = new(2, 1.0) Task;
	ptask3 = new(3, 1.5) Task;
	Task::PrintList();
	delete ptask2;
	Task::PrintList();
	delete ptask1;
	delete ptask3;
	Task::PrintList();
	return 0;
}