(C ++オブジェクトモデル):プログラム変換のセマンティクス

プログラム変換セマンティクス

  • 私たちが作成したコードの場合、コンパイラーはコードを、コンパイラーが理解して実装しやすいコードに分割します。
  • コンパイラがこれらのコードを解析する方法を見てみましょう。
  • プログラマーの視点から/コンパイラーの視点から  
  • プログラマのコードに対する見方とコンパイラのコードに対する見方は常に入れ替わっています。

定義時にオブジェクトを初期化する

class X
{
public:
	int m_i;
	X(const X& tmpx)
	{
		m_i = tmpx.m_i;
		cout << "拷贝构造函数被调用" << endl;
	}
	X()
	{
		m_i = 0;
		cout << "构造函数被调用" << endl;
	}
};

int main()
{
	
	X x0;
	x0.m_i = 15;
	X x1 = x0;        // 定义的时候初始化,调用拷贝构造函数
	X x2(x0);         // 定义的时候初始化,调用拷贝构造函数
	X x3 = (x0);      // 定义的时候初始化,调用拷贝构造函数
}
  • 出力結果:

  • コンパイラパースペクティブに切り替えます。コンパイラは2つのステップに分割されます(コンパイラパースペクティブ)
    • X x3;ステップ1:オブジェクトを定義し、オブジェクトにメモリを割り当てます。コンパイラーの観点からは、この文はXクラスのコンストラクターを呼び出しません
    • x3.X :: X(x0);ステップ2:オブジェクトのコピーコンストラクターを直接呼び出す
  • 総括する:
    • コンパイラは最初にオブジェクトを作成し、次にオブジェクトのコピーコンストラクターを呼び出してコピーします。

パラメータの初期化

class X
{
public:
	int m_i;
	X(const X& tmpx)
	{
		m_i = tmpx.m_i;
		cout << "拷贝构造函数被调用" << endl;
	}
	X()
	{
		m_i = 0;
		cout << "构造函数被调用" << endl;
	}
	~X()
	{
		cout << "析构函数被调用" << endl;
	}
};

void func(X tmpx)
{
	return;
}

int main()
{
	X x0;
	func(x0);
	return 1;
}
  • 出力結果

  • プログラマーの視点:パラメーターを渡すときは、コピーコンストラクターを呼び出してx0をtmpxにコピーします。関数が実行されると、tmpxは破棄されます。
  • 最新のコンパイラーの観点から、関数は最初にtmpxオブジェクトを作成し、次にオブジェクトのコピーコンストラクターを呼び出してx0をtmpxにコピーします。X tmpx(x0)の実行プロセスに似ています。

古いコンパイラの視点

  • X tmpobj;コンパイラーは一時オブジェクトを生成します
  • tmpobj.X :: X(x0);コピーコンストラクターを呼び出す
void func(X tmpx)
{
	return;
}
老编译器看func(老编译器角度)
void func(X &tmpx)
{
	return;
}
  • func(tmpobj);一時オブジェクトでfuncを呼び出す
  • tmpobj.X ::〜X(); func()が呼び出された後、デストラクタが呼び出されます
  • 概要:古いコンパイラーは最初に一時オブジェクトを外部で作成し、次にそれを参照によって関数に渡します(値の受け渡し方法を参照に変更します)。

戻り値の初期化

class X
{
public:
	int m_i;
	X(const X& tmpx)
	{
		m_i = tmpx.m_i;
		cout << "拷贝构造函数被调用" << endl;
	}
	X()
	{
		m_i = 0;
		cout << "构造函数被调用" << endl;
	}
	~X()
	{
		cout << "析构函数被调用" << endl;
	}
};

X func()
{
	X x0;
	//....
	return x0;   
}


int main()
{
	X my = func();
	return 1;
}
  • 出力結果(プログラマーの観点からの分析):

  • 上記のコードに対するコンパイラーの理解(コンパイラーの観点)
    • X my;オブジェクトmyを生成するだけで、Xのコンストラクターを呼び出さない
    • func(my); 
  • コンパイラーの観点からのFunc
void func(X &extra)
{
    X x0; //从编译器角度,这里不调用X的构造函数
    //...
    //...
    extra.X::X(x0);  //调用拷贝构造函数,将x0拷贝给外部传来的引用
    return;
}

クラスにメンバー関数を追加する

class X
{
public:
	int m_i;
	X(const X& tmpx)
	{
		m_i = tmpx.m_i;
		cout << "拷贝构造函数被调用" << endl;
	}
	X()
	{
		m_i = 0;
		cout << "构造函数被调用" << endl;
	}
	~X()
	{
		cout << "析构函数被调用" << endl;
	}
	void functest()
	{
		cout << "functest()被调用" << endl;
	}
};

X func()
{
	X x0;
	//....
	return x0;   
}


int main()
{
	func().functest();
	return 1;
}
  • 出力結果:

  • プログラマーの視点:
    • func()。functest();
  • コンパイラーの視点
  • X my; Xのコンストラクターを呼び出さない
  • (func(my)、my).functest();カンマ式:最初に式1を計算し、次に式2を計算します。コンマ式全体の結果は式2の値です。
  • ここで(func(my)は:
void func(X &extra)
{
    X x0; //从编译器角度,这里不调用X的构造函数
    //...
    //...
    extra.X::X(x0);  //调用拷贝构造函数,将x0拷贝给外部传来的引用
    return;
}

 

おすすめ

転載: blog.csdn.net/baidu_41388533/article/details/108670779