C++のヒープ領域について疑問があります。
1つは、reallocの存在についてです。
Bjarne StroustrupによるC++のスタイルとテクニックに関するFAQ
realloc()は、malloc()(とそれと同類の関数)が割り当てた、ユーザ定義の コピーコンストラクタを持たないオブジェクトの配列に対して動作することが保証 されているだけです。
さらに、realloc()は単純な予想に反して、場合によっては 引数の配列をコピーすることがあるということを忘れないでください。
C++でメモリの再割り当てを扱うには、vectorのような標準コンテナを使用する のがよりよい方法です。
ふむ!なるほど・・・
reallocではなく、vectorを使用しなさいと言っています。
vectorが随時伸びるのは良いのですが・・・
newを使った場合に領域を増やしたい!!と言う時はどうするんでしょうか??
**vectorはどうやってヒープ領域を随時伸長させているのか??**ここをヒントに探れば良いんでしょうかね。
というわけで調べてみました。
strace でvectorを見てみました。brkシステムコールが呼ばれています。確かにヒープ領域ですね。(ちなみに、newも同じくbrkシステムコールが呼び出されています。)
munmap(0x7fe0d82b9000, 206565) = 0 brk(NULL) = 0x55a4088ac000 brk(0x55a4088cd000) = 0x55a4088cd000 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0 write(1, "100\n", 4100 ) = 4 exit_group(0) = ? +++ exited with 0 +++
vectorのpush_back関数はemplace_back関数を呼び出していました。
その中に以下のような関数があります。
おそらくここでヒープ領域を伸ばしているのだ思われます。
#else template<typename _Tp, typename _Alloc> void vector<_Tp, _Alloc>:: _M_realloc_insert(iterator __position, const _Tp& __x) #endif { const size_type __len = _M_check_len(size_type(1), "vector::_M_realloc_insert"); const size_type __elems_before = __position - begin(); pointer __new_start(this->_M_allocate(__len)); pointer __new_finish(__new_start); __try { // The order of the three operations is dictated by the C++11 // case, where the moves could alter a new element belonging // to the existing vector. This is an issue only for callers // taking the element by lvalue ref (see last bullet of C++11 // [res.on.arguments]). _Alloc_traits::construct(this->_M_impl, __new_start + __elems_before, #if __cplusplus >= 201103L std::forward<_Args>(__args)...); #else __x); #endif __new_finish = pointer(); __new_finish = std::__uninitialized_move_if_noexcept_a (this->_M_impl._M_start, __position.base(), __new_start, _M_get_Tp_allocator()); ++__new_finish; __new_finish = std::__uninitialized_move_if_noexcept_a (__position.base(), this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator()); } __catch(...) { if (!__new_finish) _Alloc_traits::destroy(this->_M_impl, __new_start + __elems_before); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; }
少し長いですね。
また、昔は、vectorはmalloc()が使用されていたようですね。
今は、違うようです。valgrindでメモリリークを調べてみましたが、
All heap blocks were freed -- no leaks are possible
と出てくるので、まぁ心配はないでしょう。
GNU C++ ライブラリの STL コンテナクラスからレポートされるメモリリークについて
んーーーー。イマイチよくわからない・・・。
「vector内部でnewを使用している可能性があるのでは?」と思ったのですが、newとは別に独自に確保関数を作っているんですかね・・・
new式でヒープ領域を随時増やす方法ってどうやるんでしょうか??
2つ目の疑問はnewの使い方についてです。
C++11/14コア言語という書籍の中に以下のような記述を見つけました。
void* operator new(std::size_t size, int , int ,int) throw(std::bad_alloc){ return operator new(size); } void operator delte(void *ptr, int ,int ,int)throw(){ std::cout << "Placement delete" << std::endl; operator delete(ptr); } struct Fail{ Fail() noexcept(false); }; int main(){ Fail *ptr = new(1,2,3)Fail; delete ptr; }
placement newを使用しています。
これっておかしくないでしょうか??
もし、初期化で失敗したら、例外を送出しますよね。
delete(void *ptr, int, int, int)が呼び出されるので、2回の解放関数が呼び出されることになりませんか??
これは脆弱性になりますよね。
しかし、最後のdelete ptrを消すと今度は、メモリリークになる可能性が出てきてしまいます。
うーーーん。
この場合、どうするんでしょうか??
また、例外の実装の箇所に以下のうような記述があります。
std::nothrow_tは、単にオーバーロード解決のためのタグに過ぎない。また、引数として渡しているstd::nothrowは、単に便利な変数である。
namespace std{ struct nothow_t{}; extern const nothow_t nothrow; }
ん??変数の方の定義はどこにあるんです?
これって、定義がなくても使用することってできるんか??
最後の疑問はかなり初歩的です。
struct C{ C(){}; C(int){}; C(int, int){}; } int main(){ new C; new C(0); new C(0, 0); new C{0}; new C{0, 0}; }
最初の3つはコンストラクタを呼んでいるかと思います。
最後の2つです。
これは・・・コンストラクタの方に、std::initializer_listがありませんが・・・
これは、それぞれ、new C(0); new C(0, 0);と同値である。と考えて良いんですか??
分かる方教えてください。環境は、g++ Linuxです。
[追記]
vectorが伸びているのは、再度newを呼び出していました。
pointer │ │99 allocate(size_type __n, const void* = static_cast<const void*>(0)) │ │100 { │ >│101 if (__n > this->max_size()) │ │102 std::__throw_bad_alloc(); │ │103 │ │104 #if __cpp_aligned_new │ │105 if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) │ │106 { │ │107 std::align_val_t __al = std::align_val_t(alignof(_Tp)); │ │108 return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp), __al)); │ │109 } │ │110 #endif │ │111 return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); │ │112 }
おそらく、線形listのように、newを使って新たに領域を作り、そのポインタを連結させているんですかね・・・
つまり・・・newで作成した領域を随時増やす、reallocみたいな関数はnewでは実現不可能・・・ということですかね。

回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/31 08:24
2018/07/31 08:25
2018/07/31 09:21
2018/07/31 11:22 編集
2018/07/31 11:47
2018/07/31 12:03
2018/07/31 12:28