45

私は持っています

class Foo {
....
}

Fooが分離できるようにする方法はありますか?

function blah() {
  Foo foo; // on the stack
}

function blah() {
  Foo foo* = new Foo(); // on the heap
}

Fooがスタックに割り当てられているかヒープに割り当てられているかに応じて、さまざまなことができるようにしたいと思います。

編集:

多くの人が私に「なぜこれをするのか」と尋ねてきました。

答え:

現在、参照カウントされたGCを使用しています。ただし、マーク&スイープも実行できるようにしたいです。このために、一連の「ルート」ポインターにタグを付ける必要があります。これらはスタック上のポインターです。したがって、クラスごとに、それらがスタックにあるのかヒープにあるのかを知りたいと思います。

4

15 に答える 15

22

それを行うためのハッキーな方法:

struct Detect {
   Detect() {
      int i;
      check(&i);
   }

private:
   void check(int *i) {
      int j;
      if ((i < &j) == ((void*)this < (void*)&j))
         std::cout << "Stack" << std::endl;
      else
         std::cout << "Heap" << std::endl;
   }
};

オブジェクトがスタック上に作成された場合、それは外部関数のスタック変数の方向のどこかに存在する必要があります。ヒープは通常、反対側から成長するため、スタックとヒープは中央のどこかで出会うことになります。

(これが機能しないシステムは確かにあります)

于 2010-01-13T05:58:14.057 に答える
9

実際に私たちに本当の質問をする必要があります(a) :-)なぜこれが必要だと思うのかは明らかかもしれませんが、ほとんど場合そうではありません。実際、それはほとんどの場合悪い考えです。言い換えれば、なぜあなたはこれをする必要があると思いますか?

私は通常、開発者がオブジェクトが割り当てられた場所に基づいてオブジェクトを削除するか削除したくないためだと思いますが、それは通常、コード自体ではなく、コードのクライアントに任せるべきものです。


アップデート:

質問で理由を明確にしたので、お詫び申し上げます。おそらく、あなたが求めていることが理にかなっている数少ない領域の1つを見つけたでしょう(独自のガベージコレクションプロセスを実行する)。理想的には、すべてのメモリ割り当て演算子と割り当て解除演算子をオーバーライドして、ヒープから作成および削除されたものを追跡します。

deleteただし、呼び出されない状況が発生する可能性があり、マーク/スイープは参照カウントに依存するため、ポインタの割り当てをインターセプトできる必要があるため、クラスの新規/削除をインターセプトするのは簡単なことではありません。それが正しく機能するために。

あなたはそれをどのように扱うつもりかについて考えましたか?

古典的な例:

myobject *x = new xclass();
x = 0;

削除呼び出しは発生しません。

また、インスタンスの1つへのポインターがスタック上にあるという事実をどのように検出しますか?newとdeleteをインターセプトすると、オブジェクト自体がスタックベースかヒープベースかを保存できますが、特に次のようなコードでは、ポインターがどこに割り当てられるかを判断する方法がわかりません。

myobject *x1 = new xclass();  // yes, calls new.
myobject *x2 = x;             // no, it doesn't.

おそらく、手動のメモリ管理を時代遅れにするのに大いに役立つC++のスマートポインタを調べたいと思うかもしれません。共有ポインター自体は、循環依存のような問題に悩まされる可能性がありますが、弱ポインタ​​ーを適切に使用することで、それを簡単に解決できます。

シナリオでは、手動のガベージコレクションが不要になった可能性があります。


(a)これはとして知られていX/Y problemます。多くの場合、人々はあるクラスの解決策を前提とする質問をしますが、より良いアプローチは、最良の解決策が何であるかについての先入観なしに問題を説明することです。

于 2010-01-13T05:55:49.910 に答える
8

答えはノーです。これを行うための標準的/ポータブルな方法はありません。新しいオペレーターのオーバーロードを伴うハックには、穴ができる傾向があります。ポインタアドレスのチェックに依存するハックは、OS固有およびヒープ実装固有であり、OSの将来のバージョンで変更される可能性があります。あなたはそれに満足しているかもしれませんが、私はこの振る舞いを中心にいかなる種類のシステムも構築しません。

私はあなたの目標を達成するためのさまざまな方法を検討し始めます-おそらくあなたはあなたのスキームの「ルート」として機能するために完全に異なるタイプを持つことができます、またはユーザーに特別なコンストラクターでスタックに割り当てられたタイプに(適切に)注釈を付けるように要求することができます。

于 2010-01-13T07:33:09.870 に答える
8

'this'の値をスタックポインタの現在の値と比較すると可能です。この<spの場合、スタックに割り当てられています。

これを試してみてください(x86-64でgccを使用):

#include <iostream>

class A
{
public:
    A()
    {
        int x;

        asm("movq %1, %%rax;"
            "cmpq %%rsp, %%rax;"
            "jbe Heap;"
            "movl $1,%0;"
            "jmp Done;"
            "Heap:"
            "movl $0,%0;"
            "Done:"
            : "=r" (x)
            : "r" (this)
            );

        std::cout << ( x ? " Stack " : " Heap " )  << std::endl; 
    }
};

class B
{
private:
    A a;
};

int main()
{
    A a;
    A *b = new A;
    A c;
    B x;
    B *y = new B;
    return 0;
}

次のように出力されます。

Stack 
Heap 
Stack 
Stack 
Heap
于 2010-01-13T15:15:02.410 に答える
5

より直接的で邪魔にならない方法は、メモリ領域マップ(など/proc/<pid>/maps)でポインタを検索することです。各スレッドには、スタックに割り当てられた領域があります。静的変数とグローバル変数は.bssセクションにあり、定数はrodataまたはconstセグメントにあります。

于 2010-01-13T06:48:24.353 に答える
4

私はあなたが何を求めているのか肯定的ではありませんが、new演算子をオーバーライドすることがあなたがやろうとしていることかもしれません。C ++でヒープ上にオブジェクトを作成する唯一の安全な方法はnew演算子を使用することであるため、ヒープ上に存在するオブジェクトと他の形式のメモリを区別できます。詳細については、Googleの「c++での新規オーバーロード」を参照してください。

ただし、クラス内から2種類のメモリを区別することが本当に必要かどうかを検討する必要があります。保管場所によってオブジェクトの動作が異なるように注意しないと、災害のレシピのように聞こえます。

于 2010-01-13T05:55:17.003 に答える
4

上記のように、オーバーロードされたnew演算子を使用してオブジェクトがどのように割り当てられるかを制御する必要があります。ただし、2つのことに注意してください。最初に、ユーザーによって事前に割り当てられたメモリバッファ内でオブジェクトを初期化する「placementnew」演算子。次に、ユーザーが任意のメモリバッファをオブジェクトタイプにキャストすることを妨げるものは何もありません。

char buf[0xff]; (Foo*)buf;

もう1つの方法は、ほとんどのランタイムがヒープ割り当てを行うときに要求されるよりも少し多くのメモリを使用するという事実です。彼らは通常、ポインタによって適切な割り当て解除を識別するために、そこにいくつかのサービス構造を配置します。これらのパターンについてランタイム実装を検査することはできますが、それはコードを本当に移植不可能で、危険で、サポートできないやり過ぎになります。

繰り返しになりますが、前述のように、このソリューションを考案した最初の問題(「理由」)について尋ねる必要がある場合は、実際にソリューションの詳細(「方法」)を求めています。

于 2010-01-13T06:14:27.903 に答える
3

いいえ、それは確実にまたは賢明に行うことはできません。

newをオーバーロードすることで、オブジェクトがいつ割り当てられたかを検出できる場合がありますnew

しかし、オブジェクトがクラスメンバーとして構築され、所有するクラスがヒープに割り当てられている場合はどうなるでしょうか。

これがあなたが持っている2つに追加する3番目のコード例です:

class blah {
  Foo foo; // on the stack? Heap? Depends on where the 'blah' is allocated.
};

静的/グローバルオブジェクトはどうですか?スタック/ヒープのものとどのように区別しますか?

オブジェクトのアドレスを調べ、それを使用して、スタックを定義する範囲内にあるかどうかを判断できます。ただし、スタックは実行時にサイズ変更される場合があります。

つまり、実際のところ、最良の答えは、「マーク&スイープGCがC++で使用されない理由がある」ということです。適切なガベージコレクタが必要な場合は、それをサポートする別の言語を使用してください。

一方、ほとんどの経験豊富なC ++プログラマーは、リソース管理( RAII )に必要な手法を学ぶと、ガベージコレクターの必要性がほとんどなくなることに気付きます

于 2010-01-13T12:16:39.477 に答える
2

MFCクラスの方法:

.H

class CTestNEW : public CObject
{
public:
    bool m_bHasToBeDeleted;
    __declspec(thread) static void* m_lastAllocated;
public:
#ifdef _DEBUG
    static void* operator new(size_t size, LPCSTR file, int line) { return internalNew(size, file, line); }
    static void operator delete(void* pData, LPCSTR file, int line) { internalDelete(pData, file, line); }
#else
    static void* operator new(size_t size) { return internalNew(size); }
    static void operator delete(void* pData) { internalDelete(pData); }
#endif
public:
    CTestNEW();
public:
#ifdef _DEBUG
    static void* internalNew(size_t size, LPCSTR file, int line)
    {
        CTestNEW* ret = (CTestNEW*)::operator new(size, file, line);
        m_lastAllocated = ret;
        return ret;
    }

    static void internalDelete(void* pData, LPCSTR file, int line)
    {
        ::operator delete(pData, file, line);
    }
#else
    static void* internalNew(size_t size)
    {
        CTestNEW* ret = (CTestNEW*)::operator new(size);
        return ret;
    }

    static void internalDelete(void* pData)
    {
        ::operator delete(pData);
    }
#endif
};

.CPP

#include "stdafx.h"
.
.
.
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

void* CTestNEW::m_lastAllocated = NULL;
CTestNEW::CTestNEW()
{
    m_bHasToBeDeleted = (this == m_lastAllocated);
    m_lastAllocated = NULL;
}
于 2012-04-22T14:57:45.303 に答える
2

paxが尋ねるメタ質問は、「なぜそれをしたいのか」と尋ねられ、より有益な答えが得られる可能性があります。

これを「正当な理由」(おそらく単に好奇心)で行っていると仮定すると、演算子newとdeleteをオーバーライドすることでこの動作を得ることができますが、以下を含む12のバリアントすべてをオーバーライドすることを忘れないでください。

new、delete、new no throw、delete no throw、new array、delete array、new array no throw、delete array no throw、placement new、placement delete、placement new array、placementdeletearray。

できることの1つは、これを基本クラスに入れて、そこから派生させることです。

これは一種の苦痛ですが、どのような異なる行動を望みましたか?

于 2010-01-13T06:13:25.080 に答える
1

クラスのnew()をオーバーロードします。このようにして、ヒープとスタックの割り当てを区別できますが、スタックと静的/グローバルを区別することはできません。

于 2010-01-13T05:50:08.847 に答える
1

代わりにスマートポインタを使用することをお勧めします。設計上、クラスにはクラスに関するデータと情報が含まれている必要があります。簿記のタスクはクラスの外に委任する必要があります。

newとdeleteをオーバーロードすると、想像以上に多くのホールが発生する可能性があります。

于 2011-02-16T11:25:12.747 に答える
1

あなたの質問に答えるために、信頼できる方法(あなたのアプリケーションが複数のスレッドを使用していないと仮定して)、あなたのスマートポインタに含まれていないすべてのものがヒープ上にないことを前提としています:

-> newをオーバーロードして、割り当てられたすべてのブロックのリストを各ブロックのサイズとともに保存できるようにします。->スマートポインタのコンストラクタが、このポインタが属するブロックを検索します。それがどのブロックにもない場合、それは「スタック上にある」と言うことができます(実際には、それはあなたによって管理されていないことを意味します)。それ以外の場合は、ポインタがいつどこに割り当てられたかがわかります(孤立したポインタや、メモリの空き容量などを探したくない場合)。アーキテクチャに依存しません。

于 2013-03-26T21:02:37.930 に答える
1

解決策はありますが、それは継承を強制します。Meyers、「より効果的なC ++」、アイテム27を参照してください。

編集:
マイヤーズの提案は、マイヤーズ自身が彼のブログ(この投稿)でリンクしているロン・ファン・デル・ウォルによって書かれた記事に要約されています:

ヒープベースのオブジェクトの追跡

グローバル変数アプローチの代替として、Meyersは、リストを使用してヒープから割り当てられたクラスインスタンスのアドレスを追跡し、この情報を使用して特定のオブジェクトがヒープ上にあるかどうかを判断するHeapTrackedクラスを提示します。実装は次のようになります。

class HeapTracked {
  // Class-global list of allocated addresses
  typedef const void *RawAddress;
  static list<RawAddress> addresses;
public:
  // Nested exception class
  class MissingAddress {};

  // Virtual destructor to allow dynamic_cast<>; pure to make
  // class HeapTracked abstract.
  virtual ~HeapTracked()=0;

  // Overloaded operator new and delete
  static void *operator new(size_t sz)
  {
    void *ptr=::operator new(sz);
    addresses.push_front(ptr);
    return ptr;
  }

  static void operator delete(void *ptr)
  {
    // Remove ‘ptr’ from ‘addresses’
    list<RawAddress>::iterator it=find(addresses.begin(),

    addresses.end(), ptr);
    if (it !=addresses.end()) {
      addresses.erase(it);
      ::operator delete(ptr);
    } else
      throw MissingAddress();
  }

  // Heap check for specific object
  bool isOnHeap() const
  {
    // Use dynamic cast to get start of object block
    RawAddress ptr=dynamic_cast<RawAddress>(this);
    // See if it’s in ‘addresses’
    return find(addresses.begin(), addresses.end(), ptr) !=
      addresses.end();
  }
};

// Meyers omitted first HeapTracked:: qualifier...
list<HeapTracked::RawAddress> HeapTracked::addresses; 

元の記事にはさらに読むべきものがあります。RonvanderWalはこの提案についてコメントし、他の代替ヒープ追跡方法を示しています。

于 2016-06-15T06:29:05.753 に答える
0

ここでプログラムを見てください:http://alumni.cs.ucr.edu/~saha/stuff/memaddr.html。いくつかのキャストで、それは出力します:

        Address of main: 0x401090
        Address of afunc: 0x401204
Stack Locations:
        Stack level 1: address of stack_var: 0x28ac34
        Stack level 2: address of stack_var: 0x28ac14
        Start of alloca()'ed array: 0x28ac20
        End of alloca()'ed array: 0x28ac3f
Data Locations:
        Address of data_var: 0x402000
BSS Locations:
        Address of bss_var: 0x403000
Heap Locations:
        Initial end of heap: 0x20050000
        New end of heap: 0x20050020
        Final end of heap: 0x20050010
于 2013-03-27T06:57:51.907 に答える