C++ 初級 - コピー構築と演算子のオーバーロード (const メンバー)

目次

1. コンストラクターのコピー

1.2 コピー コンストラクターの機能:

2. デフォルトのコピーコンストラクター

2.1 明示的に定義されていない場合、コンパイラはデフォルトのコピー コンストラクターを生成します。デフォルトのコピー コンストラクター オブジェクトは、メモリ ストレージに従ってバイト オーダーでコピーされます。この種のコピーは、浅いコピー、または値コピーと呼ばれます。

3. 演算子のオーバーロード

3.1 演算子のオーバーロードに関する考え方

注: 代入演算子はクラスのメンバー関数としてのみオーバーロードでき、グローバル関数としてオーバーロードできません。

3.2 プレ++とポスト++

pre-++ と post-++ を区別するために、C++ では post-++ の関数に int 型パラメータを追加して pre-++ を区別します。

4. const メンバー

 4.1 アドレス演算子と定数アドレス演算子


1. コンストラクターのコピー

コピー コンストラクター: このクラス型のオブジェクトへの参照である仮パラメーターが 1 つだけあり (通常は const 装飾が一般的に使用されます)、既存のクラス型オブジェクトを使用して新しいオブジェクトを作成するときにコンパイラーによって自動的に呼び出されます

 

class Date
{
public:
	 Date(int year = 1900, int month = 1, int day = 1)
	 {
	 _year = year;
	 _month = month;
	 _day = day;
	 }
private:
	 int _year;
	 int _month;
	 int _day;
};
int main()
{
	 Date d1(2023,7,30);
	 Date d2(d1);//用d1初始化d2
	 return 0;
}

:ここでは既存のオブジェクトを初期化しています

1.2 コピー コンストラクターの機能:

  • コピー構築はコンストラクターのオーバーロードされた形式です
  • コピー コンストラクターはパラメーターを 1 つだけ持つことができ、クラス型オブジェクトへの参照である必要があります。値渡しメソッドを使用すると、無限の再帰呼び出しが発生する
    ため、コンパイラーはエラーを直接報告します。
class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    // Date(const Date& d) // 正确写法
    Date(const Date d) // 错误写法:编译报错,会引发无穷递归
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1;
    Date d2(d1);
    return 0;
}

書き方を間違えるとレイヤーごとに呼び出すと無限ループになってしまいます。 

したがって、構造をコピーするときに参照を追加する必要があります


2. デフォルトのコピーコンストラクター

ユーザーがコピー コンストラクターを明示的に作成しない場合、コンパイラーはデフォルトのコンストラクターを自動的に生成します。

デフォルトのコンストラクターは組み込み型に対して実行されます。

值拷贝(浅拷贝)

调用自定义类型的构造函数完成拷贝

2.1 明示的に定義されていない場合、コンパイラはデフォルトのコピー コンストラクターを生成しますデフォルトのコピー コンストラクター オブジェクトは、メモリ ストレージに従ってバイト オーダーでコピーされます。この種のコピーは、浅いコピー、または値コピーと呼ばれます。

class Time
{
public:
    Time()
    {
        _hour = 1;
        _minute = 1;
        _second = 1;

    }
    Time(const Time& t)
    {
        _hour = t._hour;
        _minute = t._minute;
        _second = t._second;
        cout << "Time::Time(const Time&)" << endl;
    }
private:
    int _hour;
    int _minute;
    int _second;
};

class Date
{
private:

    // 基本类型(内置类型)
    int _year = 1970;
    int _month = 1;
    int _day = 1;


    // 自定义类型
    Time _t;
};
int main()
{

    Date d1;
  
    // 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
    // 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构
    造函数

    Date d2(d1);
    return 0;
}

注: コンパイラによって生成されるデフォルトのコピー コンストラクターでは、組み込み型はバイト単位で直接コピーされますが、
カスタムコピー コンストラクターを呼び出すことによってコピーされます。


そのような例を見てみましょう。

class Stack
{
public:
 Stack(size_t capacity = 10)//构造函数
 {
	 _array = (DataType*)malloc(capacity * sizeof(DataType));
	 if (nullptr == _array)
	 {
	 perror("malloc申请空间失败");
	 return;
	 }
	 _size = 0;
	 _capacity = capacity;
 }
 void Push(const int& data)//插入函数
 {
	 _array[_size] = data;
	 _size++;
 }
 
 ~Stack()//析构函数
 {
	 if (_array)
	 {
		 free(_array);
		 _array = nullptr;
		 _capacity = 0;
		 _size = 0;
	 }
 }

private:
	 int *_array;
	 size_t _size;
	 size_t _capacity;
};

int main()
{
	Stack s1(10);
	s1.push(1);
	s1.push(2);
	s3.push(3);
	s3.push(4);
	s3.push(5);
	Stack s2(s1);
	retrn 0;
}

 配列が指す空間を動的に開いてヒープ領域に格納し、値コピー方式を使用することで
s1とs2の配列が指す空間を同一にする!

s1 と s2 のライフサイクルが終了すると、それぞれのデストラクターがそれぞれ呼び出されます
が、2 つのオブジェクト内のポインターは同じ空間を指します。
析构函数会调用两个free释放空间!同一份空间释放两个就会出错!


3. 演算子のオーバーロード

C++ では、コードの可読性を高めるために演算子のオーバーロードが導入されています。演算子のオーバーロードは、特殊な関数名を持つ関数であり、戻り値の型、
関数名、およびパラメーターのリストも存在します。戻り値の型とパラメーターのリストは、通常の関数と似ています。

関数名は、キーワード演算子の後に、オーバーロードする必要がある演算子記号が続きます。
関数プロトタイプ:戻り値型演算子演算子(パラメータリスト)

知らせ:

  • 他の記号を連結して新しい演算子を作成することはできません: 例:operator@        
  • オーバーロードされた演算子にはクラス型パラメータが必要です
  • 組み込み型に使用される演算子。その意味は変更できません。例: 組み込み整数 +。その意味は変更できません。
  • クラスのメンバー関数としてオーバーロードされると、メンバー関数の最初のパラメーターが非表示のパラメーターであるため、その仮パラメーターはオペランドの数より 1 少ないように見えます。
  • .* :: sizeof ?: . 上記の 5 つの演算子はオーバーロードできないことに注意してください。これは記述式の多肢選択問題でよく出てきます。

関数プロトタイプ:

戻り値の型    演算子 演算子(パラメータリスト)

Date operator+(Date d1, int x);

3.1 演算子のオーバーロードに関する考え方

演算子のオーバーロードはカスタム型用であるため、関数パラメータにクラス型パラメータが必要です

演算子のオーバーロードがクラスの外に書かれている場合、クラスのプライベート メンバーにアクセスできません。

class Date
{ 
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
        _year = year;
        _month = month;
        _day = day;
 }
 bool operator==(const Date& d2)
 {
	  return _year == d2._year;
	      && _month == d2._month
	      && _day == d2._day;
 }
private:
 int _year;
 int _month;
 int _day;
};

クラス内の関数にはデフォルトで this ポインターがあるため、ポインター this はこのタイプのオブジェクトを表します

注: 代入演算子はクラスのメンバー関数としてのみオーバーロードでき、グローバル関数としてオーバーロードできません。
 

// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{
    if (&left != &right)
    {
        left._year = right._year;
        left._month = right._month;
        left._day = right._day;
    }
    return left;
}

// 编译失败:
// error C2801: “operator =”必须是非静态成员

理由: 代入演算子が明示的に実装されていない場合、コンパイラはデフォルトの代入演算子を生成します。このとき、ユーザーがクラスの外で
グローバル代入演算子オーバーロード
代入演算子オーバーロードはクラスのメンバー関数のみにすることができます。

3.2 プレ++とポスト++

pre-++ と post-++ を区別するために、C++ では post-++ の関数に int 型パラメータを追加して pre-++ を区別します。
//前置++
Date& operator++();


//后置++
Date& operator++(int);

注: post ++ にはもう 1 つのパラメータがありますが、このパラメータはまったく役に立ちません! pre ++ を区別するためにのみ使用されます。

例を挙げてみましょう:

class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    // 前置++:返回+1之后的结果
    // 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
    Date& operator++()
    {
        _day += 1;
        return *this;
    }

    // 后置++:
    // 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
    // C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
自动传递
    // 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存
一份,然后给this+1
    // 而temp是临时对象,因此只能以值的方式返回,不能返回引用
    Date operator++(int)
    {
        Date temp(*this);
        _day += 1;
        return temp;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d;
    Date d1(2022, 1, 13);
    d = d1++; // d: 2022,1,13 d1:2022,1,14
    d = ++d1; // d: 2022,1,15 d1:2022,1,15
    return 0;
}

4. const メンバー

const 修飾された「メンバー関数」は const メンバー関数と呼ばれます。const 修飾されたクラス メンバー関数は実際にメンバー関数の
暗黙的な this ポインターを変更し、クラスのメンバーがメンバー関数内で変更できないことを示します。

 4.1 アドレス演算子と定数アドレス演算子

これら 2 つのデフォルトのメンバー関数は通常、再定義する必要はなく、コンパイラーはデフォルトで生成します。
 

class Date
{
public :
    Date* operator&()
    {
        return this ;
    }
    const Date* operator&()const
    {
        return this ;
    }
private :
    int _year ; // 年
    int _month ; // 月
    int _day ; // 日
};

これら 2 つの演算子は通常、オーバーロードする必要はありません。他の演算子に指定されたコンテンツを取得させたい場合など
特殊な場合にのみ、コンパイラーによって生成されたデフォルトのアドレス取得オーバーロードを使用するだけです。
 

おすすめ

転載: blog.csdn.net/m0_74459304/article/details/132130459