22

私は次のクラスを持っています:

class BritneySpears
{
  public:

    int getValue() { return m_value; };

  private:

    int m_value;
};

これは外部ライブラリです (変更できません)。私は明らかに の値を変更することはできず、m_valueそれを読むだけです。から派生しても機能しBritneySpearsません。

次のクラスを定義するとどうなりますか。

class AshtonKutcher
{
  public:

    int getValue() { return m_value; };

  public:

    int m_value;
};

そして、次のようにします。

BritneySpears b;

// Here comes the ugly hack
AshtonKutcher* a = reinterpret_cast<AshtonKutcher*>(&b);
a->m_value = 17;

// Print out the value
std::cout << b.getValue() << std::endl;

私はこれが悪い習慣であることを知っています。しかし、好奇心から:これは動作することが保証されていますか? それは定義された動作ですか?

おまけの質問: こんな醜いハックを使わなければならなかったことはありますか?

編集: 怖がらせる人を少なくするため: 実際のコードで実際にこれを行うつもりはありません。ちょっと思ったんです ;)

4

6 に答える 6

21

これは未定義の動作です。各アクセス修飾子セクション内のメンバーは、出現順に配置されることが保証されていますが、アクセス修飾子間ではそのような保証はありません。たとえば、コンパイラがすべてのパブリック メンバーの前にすべてのプライベート メンバーを配置することを選択した場合、上記の 2 つのクラスは異なるレイアウトになります。

編集:この古い回答を再検討すると、かなり明白な点を見逃していることに気付きました: 構造体の定義には、それぞれ正確に 1 つのデータ メンバーがあります。メンバー関数はクラスのレイアウトに影響しないため、メンバー関数の順序は関係ありません。両方のデータ メンバーが同じ場所にあることが保証されていることに気付くかもしれませんが、私は確実に言えるほど標準を知りません。

しかし!reinterpret_cast関係のない型間で ingの結果を逆参照することはできません。まだUBです。少なくとも、それはhttp://en.cppreference.com/w/cpp/language/reinterpret_castの私の読みであり、実際には危険な読みです。

于 2010-05-14T13:25:33.300 に答える
9

Marceloが指摘した理由から、これは未定義の動作です。しかし、変更できない外部コードを統合する場合、そのような手段に頼る必要がある場合があります。それを行うより簡単な方法(および同様に未定義の動作)は次のとおりです。

#define private public
#include "BritneySpears.h"
于 2010-05-14T13:30:23.587 に答える
4

のライブラリを変更できない場合がありBritneySpearsますが、.h ヘッダー ファイルは変更できるはずです。もしそうなら、あなたは次AshtonKutcherの友達を作ることができますBritneySpears

class BritneySpears 
{
    friend class AshtonKutcher;
  public: 

    int getValue() { return m_value; }; 

  private: 

    int m_value; 
}; 

class AshtonKutcher 
{ 
  public: 

    int getValue(const BritneySpears & ref) { return ref.m_value; }; 
}; 

私はこのトリックを本当に容認することはできませんし、自分で試したこともないと思いますが、合法的に明確に定義された C++ である必要があります。

于 2010-05-14T15:27:55.063 に答える
2

@Marcelo は正しいです。メンバーの順序は、異なるアクセス レベル間で定義されていません。

ただし、次のコードを検討してください。ここでAshtonKutcherは、 とまったく同じレイアウトを持っていますBritneySpears:

class AshtonKutcher
{
  public:
    int getValue() { return m_value; };
    friend void setValue(AshtonKutcher&, int);

  private:
    int m_value;
};

void setValue(AshtonKutcher& ac, int value) {
    ac.m_Value = value;
}

これは実際には有効な C++ である可能性があると思います。

于 2010-05-14T13:37:04.803 に答える
2

コードに問題があり、回答の下線が引かれています。問題は、値の順序付けにあります。

しかし、あなたはほとんどそこにいました:

class AshtonKutcher
{
public:

  int getValue() const { return m_value; }
  int& getValue() { return m_value; }

private:
  int m_value;
};

同じ属性があり、同じ順序で宣言され、同じアクセス権があり、どちらのオブジェクトにも仮想テーブルがないため、まったく同じレイアウトになります。

したがって、秘訣はアクセスレベルを変更することではなく、メソッドを追加することです:)

もちろん、私が何かを逃した場合を除きます。

メンテナンスの悪夢だと思いましたか?

于 2010-05-14T16:51:03.500 に答える
1

通常、 reinterpret_cast の使用は避けるべきであり、移植可能な結果が得られる保証はありません。

また、なぜ非公開メンバーを変更したいのですか? 元のクラスを新しいクラスでラップし (継承よりも合成を優先)、必要に応じて getValue メソッドを処理できます。

于 2010-05-14T13:27:25.060 に答える