10

SOに投稿されたいくつかの質問からこの段落について読みました。

memcpyが非 POD タイプに対して安全であることが保証されていない理由がよくわかりません。私の理解では、それmemcpyは単なるビット単位のコピーです。

以下、スタンダードからの引用

typeの任意のオブジェクト (基本クラスのサブオブジェクト以外) の場合、オブジェクトがPODtypeTの有効な値を保持しているかどうかにかかわらず、オブジェクトを構成する基礎となるバイト (1.7) をまたは.41)Tの配列にコピーできます。またはの配列の内容がオブジェクトにコピーされた後、オブジェクトは元の値を保持します。charunsigned charcharunsigned char

# define N sizeof (T)
char buf[N];
T obj ; // obj initialized to its original value
std :: memcpy (buf , & obj , N); // between these two calls to std::memcpy,
                                 // obj might be modified
std :: memcpy (& obj , buf , N); // at this point, each subobject of obj of 
                                 // scalar type holds its original value
4

5 に答える 5

17

ビットごとのコピーを試してくださいstd::shared_ptr<>。あなたのプログラムがあなたの目の前で頻繁に爆発することに気付くかもしれません。

この問題は、コピー コンストラクターがビット単位のコピー以外のことを行うすべてのクラスで発生します。の場合std::shared_ptr<>、ポインターはコピーされますが、参照カウントはインクリメントされないため、共有オブジェクトとその参照カウントを早期にshared_ptr解放し、コピーされたオブジェクトが解放された参照カウントを減らそうとすると爆発します。 .


更新:これは質問にまったく答えていないことが指摘されましたが、これは公平です。なぜなら、shared_ptr を char[] にコピーしてから戻すのではなく、shared_ptr にコピーするという考えに主に取り組んだからです。ただし、原則は引き続き適用されます。

shared_ptr を char[] にビットごとにコピーし、shared_ptr に別の値を割り当ててから、char[] をコピーして戻すと、最終的に 1 つのオブジェクトがリークし、別のオブジェクト、つまり UB が二重に削除される可能性があります。

同じことが POD でも発生する可能性がありますが、それはプログラム ロジックのバグです。変更された shared_ptr に相当する POD にビットごとにコピーして戻すことは、プログラムがそのようなイベントを理解して対応する限り、完全に有効です。std::shared_ptr に対してこれを行うと、通常は機能しません。

于 2013-07-23T12:33:00.880 に答える
4

一般に問題は、オブジェクトがデータだけでなく動作も導入することです。

データを手動でコピーすると、コピー コンストラクターに依存している可能性があるオブジェクトの固有の動作が壊れる可能性があります。

良い例は、共有または一意のポインターです。それをコピーすると、そのクラスを使用したときにそのクラスで行った「取引」が破棄されます。

コピー プロセスが意味的に正しいかどうかに関係なく、その背後にある考え方は間違っており、オブジェクト プログラミングのパラダイムに違反しています。

サンプルコード:

/** a simple object wrapper around a pthread_mutex
 */
class PThreadMutex
{
   public:
    /** locks the mutex. Will block if mutex is already locked */
    void lock();

    /** unlocks the mutex. undefined behavior if mutex is unlocked */
    void unlock();

   private:
    pthread_mutex_t m_mutex;

};

/** a simple implementation of scoped mutex lock. Acquires and locks a Mutex on creation,
 * unlocks on destruction
 */
class ScopedLock
{
  public:
    /** constructor specifying the mutex object pointer to lock
     * Locks immediately or blocks until lock is free and then locks
     * @param mutex the mutex pointer to lock
     */
    ScopedLock ( PThreadMutex* mutex );

    /** default destructor. Unlocks the mutex */
    ~ScopedLock ();

    /** locks the mutex. Will block if mutex is already locked */
    void unlock();


  private:

    PThreadMutex* m_mutex;

    // flag to determine whether the mutex is locked
    bool m_locked;

    // private copy constructor - disable copying
    ScopedLock(ScopedLock &mutex) { (void)mutex; /* to get rid of warning */ };

};

クラスをコピーScopedLockし、手動でロックを解除してから値を復元し、コンストラクターで別のロック解除を実行すると、未定義の動作が発生します (または、少なくともデストラクタで EPERM エラーが発生します)。

于 2013-07-23T12:39:05.020 に答える
1

C++11 の注:質問の引用は、かなり古いバージョンのルールです。C++11 以降、要件は自明にコピー可能であり、 PODよりもはるかに弱いです。


memcpy任意のオブジェクトから使用できます。オブジェクトのビットごとのイメージを取得します。

オブジェクトが POD でない場合、イメージを元のオブジェクトと同じタイプであるかのように使用することはできません。これは、ライフタイム ルールが初期化を最初に完了する必要があるためです。

このような場合、画像は単なるバイトの集まりです。たとえば、時間の経過に伴うオブジェクトの内部表現の変化を検出するなど、これはまだ有用かもしれませんが、バイトに対して有効な操作 (2 つの画像間の比較など) のみが正当であり、元の型のオブジェクトを必要とする操作ではありません。

于 2013-07-23T13:43:38.627 に答える
1

たとえば、Stringクラスを作成しているとします。クラスのインスタンスは、動的に割り当てられたchar配列へのポインターを保持する必要があります。そのようなインスタンスを memcopy すると、2 つのポインターは等しくなります。1 つの文字列を変更すると、他の文字列に影響します。

于 2013-07-23T12:33:25.543 に答える