C++のオブジェクトポインタとオブジェクト配列の詳細

7309 ワード

C++オブジェクトポインタオブジェクトへのポインタ
オブジェクトを作成すると、コンパイルシステムは各オブジェクトに一定のストレージスペースを割り当ててメンバーを格納します.オブジェクト空間の先頭アドレスがオブジェクトのポインタです.オブジェクトのポインタを格納するポインタ変数を定義できます.
クラスがある場合:

class Time
{
  public :
  int hour;
  int minute;
  int sec;
  void get_time( );
};
void Time::get_time( )
{
  cout< 
 

これに基づいて、次の文があります.

  Time *pt; //  pt   Time        
  Time t1; //  t1 Time   
  pt=&t1; // t1       pt

このようにptは、オブジェクトt 1を指すTimeクラスオブジェクトへのポインタ変数である.
クラスオブジェクトを指すポインタ変数を定義する一般的な形式は、次のとおりです.

     *     ;

オブジェクトポインタを使用して、オブジェクトとオブジェクトのメンバーにアクセスできます.次のようになります.

  *pt  //pt      , t1
  (*pt).hour //pt        hour  , t1.hour
  pt->hour //pt        hour  , t1.hour
  (*pt).get_time ( )  //  pt        get_time  , t1.get_time
  pt->get_time ( ) //  pt        get_time  , t1.get_time

上の2,3行目の役割は等価であり,4,5行目も等価である.オブジェクトメンバーへのポインタ
オブジェクトにはアドレスがあり、オブジェクトの初期アドレスを格納するポインタ変数は、オブジェクトを指すポインタ変数です.オブジェクト内のメンバーにもアドレスがあり、オブジェクトメンバーのアドレスを格納するポインタ変数は、オブジェクトメンバーを指すポインタ変数です.
1)オブジェクト・データ・メンバーへのポインタオブジェクト・データ・メンバーへのポインタ変数を定義する方法は、通常の変数へのポインタ変数を定義する方法と同じです.例:

  int *p1; //             

オブジェクト・データ・メンバーへのポインタ変数を定義する一般的な形式は、次のとおりです.

        *     ;

Timeクラスのデータ・メンバーhourが共通の整数データである場合、オブジェクト・データ・メンバーを指すポインタ変数を使用して、クラス外でオブジェクト・データ・メンバーhourにアクセスできます.

  p1=&t1.hour; //   t1     hour     p1,p1  t1.hour
  cout< 
 

2)オブジェクトメンバー関数を指すポインタは、オブジェクトメンバー関数を指すポインタ変数を定義する方法と、一般関数を指すポインタ変数を定義する方法が異なることに注意する必要があります.ここで、通常の関数を指すポインタ変数の定義方法を再確認します.データ型名(*ポインタ変数名)(パラメータテーブル列);例えばvoid(*p)();//pはvoid型関数を指すポインタ変数であり、ポインタ変数によって関数を呼び出すことができる:p=fun;//fun関数の人口アドレスをポインタ変童pに渡すと、pは関数fn(*P)();//fn関数の呼び出し
オブジェクトメンバー関数を指すポインタ変数を定義するのは複雑です.上記の方法を真似てオブジェクトメンバー関数名をポインタに割り当てると、次のようになります.

  p = t1.get_time;

コンパイルエラーが発生します.どうしてですか.
メンバー関数は通常の関数と最も根本的な違いがあります.これはクラスのメンバーです.コンパイルシステムは、上の付与文で、ポインタ変数のタイプが付与番号の右側の関数のタイプと一致しなければならないことを要求し、以下の3つの面で一致しなければならない:1関数パラメータのタイプとパラメータの個数;②関数の戻り値の種類③所属するクラス.
現在3点目のうち①②2点目は一致しているが、③点目は一致していない.ポインタ変数pはクラスに関係なく、面get_time関数はTimeクラスに属します.したがって、通常の関数とメンバー関数の異なる性質を区別するには、クラス外で直接メンバー関数名を関数エントリアドレスとしてメンバー関数を呼び出すことはできません.
では、メンバー関数を指すポインタ変数をどのように定義すればいいのでしょうか.以下の形式を採用する必要があります.

  void (Time::*p2)( ); //  p2   Time             

注意:(Time::*p 2)両側のカッコは省略できません.()の優先度が*より高いためです.かっこがない場合は、次のようになります.

  void Time::*(p2()) //      void      

共通メンバー関数へのポインタ変数を定義する一般的な形式は、次のとおりです.

        (  ::*     )(    );

共通メンバー関数を指すことができます.共通メンバー関数のエントリアドレスを共通メンバー関数を指すポインタ変数に割り当てるだけでいいです.次のようになります.

  p2=&Time::get_time;

ポインタ変数を共通メンバー関数に指す一般的な形式は

       =&  ::     ;

VC++システムでは、C言語の使い方と一致するように&を書かないこともできますが、C++プログラムを書くときは省略しないことをお勧めします&.
[例]オブジェクトポインタの使い方について.

#include 
using namespace std;
class Time
{
  public:
  Time(int,int,int);
  int hour;
  int minute;
  int sec;
  void get_time( );
};
Time::Time(int h,int m,int s)
{
  hour=h;
  minute=m;
  sec=s;
}
void Time::get_time( ) //        
//        
{
  cout<get_time( ); //  p2     ( t1) get_time  
  void (Time::*p3)( ); //    Time            p3
  p3=&Time::get_time; // p3  Time       get_time
  (t1.*p3)( ); //    t1 p3       ( t1.get_time( ))
  return 0;
}

プログラムの実行結果は次のとおりです.

10 (main   4    )
10:13:56 (main   5    )
10:13:56 (main   7    )
10:13:56 (main   10    )

t 1におけるhour,minute,secの値を出力するために,3つの異なる方法が用いられることがわかる.
いくつかの説明:1)main関数の9行目から分かるように,メンバー関数のエントリアドレスの正確な書き方は:

  &  ::     

次のように書くべきではありません.

  p3 =&t1.get_time; //t1    

メンバー関数は、オブジェクトのスペースに格納されるのではなく、オブジェクトの外のスペースに格納されます.同じクラスのオブジェクトが複数ある場合は、同じ関数コードセグメントを共有します.したがって、ポインタ変数p 3に付与されるのは、この共通の関数コードセグメントのエントリアドレスであるべきである.
t 1のget_を呼び出すtime関数はt 1を用いることができる.get_time()形式は、論理的な観点から、オブジェクト名でメンバー関数を呼び出すことができます.プログラム文に必要なアドレスは物理的で、特定のアドレスはオブジェクトではなくクラスに関連付けられています.
2)main関数8,9の2行を1行にまとめることができます.

  void (Time::*p3)( )=&Time::get_time; //            

C++オブジェクト配列配列配列は、単純な変数(例えば、整数配列の各要素が整数変数である)のみならず、オブジェクト(オブジェクト配列の各要素が同類のオブジェクト)から構成されてもよい.
日常生活の中で、多くのエンティティの属性は共通しているが、属性の具体的な内容は異なる.例えば、1つのクラスには50人の学生がおり、各学生の属性には名前、性別、年齢、成績などが含まれている.学生ごとにオブジェクトを作成する場合は、それぞれ50個のオブジェクト名を取得する必要があります.プログラムで処理するのは不便だ.この場合、各配列要素が「学生クラス」オブジェクトである「学生クラス」オブジェクトの配列を定義できます.たとえば

  Student stud[50]; //      Student ,  stud  , 50   

配列を作成するときも、コンストラクション関数を呼び出します.50個の要素がある場合は、50回のコンストラクション関数を呼び出す必要があります.
必要に応じて配列を定義するときに実パラメータを提供して初期化を実現できます.コンストラクション関数にパラメータが1つしかない場合は、配列を定義するときに、等号の後ろのカッコ内に直接実パラメータを指定できます.のように

 Student stud[3]={60,70,78}; //  ,3        3          

コンストラクション関数に複数のパラメータがある場合、配列を定義するときにすべての実パラメータを直接提供する方法は使用できません.1つの配列に複数の要素があり、各要素に複数の実パラメータを提供しなければならないためです.コンストラクション関数にデフォルトのパラメータがある場合を考慮すると、実パラメータと形パラメータの対応関係が不明確になり、曖昧性が発生しやすくなります.たとえば、クラスStudioのコンストラクション関数には複数のパラメータがあり、デフォルトのパラメータです.

Student:: Student(int=1001,int=18,int=60); //      ,     ,      

オブジェクト配列を定義する文が

  Student stud[3]={1005,60,70};

プログラムではこのような曖昧性を引き起こしやすい方法を採用しないほうがよい.
コンパイルシステムは、オブジェクト要素ごとのコンストラクション関数に1つの実パラメータのみを渡すため、配列を定義するときに提供される実パラメータの数は、配列要素の数を超えてはいけません.

  Student stud[3]={60,70,78,45}; //   ,              

では、コンストラクション関数に複数のパラメータがある場合、オブジェクト配列を定義する際にどのように初期化を実現すればよいのでしょうか.答えは、カッコにコンストラクション関数をそれぞれ書き出し、実パラメータを指定します.
コンストラクション関数に3つのパラメータがある場合は、学号、年齢、成績を表します.オブジェクト配列は、次のように定義できます.

Student Stud[3]={ //      
  Student(1001,18,87), //   1        ,    3   
  Student(1002,19,76), //   2        ,    3   
  Student(1003,18,72) //   3        ,    3   
};

オブジェクト配列を作成するときに、コンストラクション関数をそれぞれ呼び出し、各要素を初期化します.各要素の実パラメータはそれぞれかっこで囲まれており、構造関数のパラメータのセットに対応しており、混同されません.
[例]オブジェクト配列の使い方.

#include 
using namespace std;
class Box
{
public :
  //            ,               
  Box(int h=10,int w=12,int len=15): height(h),width(w),length(len){ }
  int volume( );
private :
  int height;
  int width;
  int length;
};
int Box::volume( )
{
  return (height*width*length);
}
int main( )
{
  Box a[3]={ //      
   Box(10,12,15), //      Box,   1      
   Box(15,18,20), //      Box,   2      
   Box(16,20,26) //      Box,   3      
  };
  cout< 
 

実行結果は次のとおりです.

volume of a[0] is 1800
volume of a[1] is 5400
volume of a[2] is 8320