1

私はまだ C++ を学んでいます。ポリモーフィズムがどのように機能するかを試していたところ、仮想メソッドを呼び出すときにセグメンテーション違反が発生しました。

(注:デストラクタを仮想としてマークしませんでした。何が起こるかを確認しようとしていました。)コードは次のとおりです。

#include <iostream>

using namespace std;

class Base
{
protected:
  char *name;

public:
  Base(char *name)
  {
    cout << name << ": Base class cons" << endl;
  }

  ~Base()
  {
    cout << name << ": Base class des" << endl;
  }

  virtual void disp();
};

void Base::disp()
{
  cout << name << ": Base disp()" << endl;
}

class Child : public Base
{
public:
  Child(char *name):
    Base(name)
  {
    cout << name << ": Child class cons" << endl;
  }

  ~Child()
  {
    cout << name << ": Child class des" << endl;
  }

  virtual void disp()
  {
    cout << name << ": Child disp()" << endl;
  }
};


int main()
{
  //Base b;
  //b.disp();
  Base c = Child("2");
  c.disp();
}

また、Java でこれらの概念を知っている人のために、一般的な継承とポリモーフィズムの使用法に関するその他のヒントがあれば、私に知らせてください。ありがとうございました!

4

7 に答える 7

8

name - Base で初期化されていません

また、別の問題があります:

  Base c = Child("2");

私はそれがあなたが望むものではないと思います。コードは、キャストされた Child から Base のインスタンスを作成します。しかし、Base インターフェイスに基づいて Child インスタンスを操作したいと思います。代わりに次のように書く必要があります。

  Base *c = new Child("2");

また、将来のバグを回避するために、base でデストラクタを virtual として宣言します。

于 2009-04-02T16:56:45.180 に答える
4

ベースnenber変数を初期化することはありません-ベースコンストラクターは次のようにする必要があります。

Base(char * aname) : name( aname )
  {
    cout << name << ": Base class cons" << endl;
  }

それだけでなく、あなたが言うとき

Base b = Child( "xxx" );

Child インスタンスは Base にスライスされますが、これはおそらくあなたが望むものではありません。

于 2009-04-02T16:57:28.423 に答える
1

メンバーの char * name を ctors の何かに割り当てているとは思いません。

于 2009-04-02T16:54:51.220 に答える
1

おっと。

いくつかの問題がありますが、segfault はおそらくchar*-- を渡しているためです。これは単なるポインターでありcoutdisp(). 問題は、ポインタが に存在せず、disp()に存在することmain()です。おそらく、 をディープ コピーするかchar*、 を使用する必要がありますstd::string。このようにするとうまくいきません。

編集

編集2を参照してください

nameクラスの変数に名前を割り当てることはできません。それを行うと、予測できない結果が得られます-おそらくまだセグメンテーション違反になります。覚えておいてください: C/C++ では、ヒープに割り当てられない限り、オブジェクトはローカルにスコープされます。この場合、ctor で次のようにします。

this->name = new char[ strlen( name ) + 1 ];
strcpy( this->name, name );

そして、デストラクタでは、次のようなことをしたいと思うでしょう:

delete [] this->name;

注: 私の構文は完全に間違っている可能性があります。NULL でないことを確認するchar*ために をチェックしておらず、戻り値をチェックしていないため、上記のコードは本質的に安全ではないことに気付きnew.ました。

EDIT 2: 私は修正された立場です。文字列リテラルは定数ストレージとして扱われるため、プログラムの期間中存続します。それにもかかわらず、教訓は重要だと私は信じています。一般に、文字列リテラルを処理しない場合、ポインター (または配列など) を渡す場合は、ストレージを割り当ててディープコピーする必要があります。上記のオブジェクトを破棄するときは、適切に割り当てを解除する必要もあります。

于 2009-04-02T16:56:32.060 に答える
1

Child::disp() メソッドが呼び出されることはありません。c は Base 型の変数であり、ポインターや参照ではないため、仮想メソッドはチェックされません。

Base * c = new Child("1");
c->disp();
delete c;

Child::disp() を呼び出します。

于 2009-04-02T16:59:29.277 に答える
0

コードにいくつかの問題があります。

まず、セグメンテーション違反が発生する理由は、Base ctor の実装がクラスのメンバー変数の 1 つと同じ名前のパラメーターを取ることです。

class Base
{
protected:
  char *name;

public:
  Base(char ***name**)
  {
    cout << name << ": Base class cons" << endl;
  }

ctor のパラメータ 'name'は、同じ、erm... name のクラス' メンバー変数を隠します。

次に、ここでオブジェクトをスライスしています。

int main()
{
  //Base b;
  //b.disp();
  Base c = Child("2");
  c.disp();
}

'c' は Base タイプで、Child をそれに割り当てようとしています。ogbject を基本クラスに割り当てると、Child に固有のものはすべて切り捨てられます。

これらの問題の両方を修正するコードを次に示します。

#include <iostream>
#include <string>

using namespace std;

class Base
{
protected:
    std::string name_;

public:
  Base(char *name)
      : name_(name) {
    cout << name_ << ": Base class cons" << endl;
  }

  ~Base()
  {
    cout << name_ << ": Base class des" << endl;
  }

  virtual void disp();
};

void Base::disp()
{
  cout << name_ << ": Base disp()" << endl;
}

class Child : public Base
{
public:
  Child(char *name):
    Base(name)
  {
    cout << name_ << ": Child class cons" << endl;
  }

  ~Child()
  {
    cout << name_ << ": Child class des" << endl;
  }

  virtual void disp()
  {
    cout << name_ << ": Child disp()" << endl;
  }
};


int main()
{
  //Base b;
  //b.disp();
  Base * c = new Child("2");
  c->disp();
  delete c;
}
于 2009-04-02T17:26:17.543 に答える