1関数呼び出しメソッド
1.1通常のメンバー関数を呼び出す方法
クラスメンバー関数を呼び出すコストは通常の関数を呼び出すコストよりも高くなると考えられるかもしれませんが、そうではありません。通常のメンバー関数の呼び出しはグローバル関数に似ています。VSでデバッグして逆アセンブリコードを表示できます。通常のメンバー関数が呼び出されると、コンパイラーはオブジェクトのthisポインターを渡します
1.2仮想関数を呼び出す方法
例えば:
#include<iostream>
using namespace std;
class A
{
public:
int a;
virtual void func()
{
}
void test1()
{
func(); //由于是用this指针调用func函数,所以会查询虚函数表
}
void test()
{
A::func(); //这种方式调用func函数不会查询虚函数表
}
};
int main()
{
A a = A();
a.func(); //不会查询虚函数表
a.test(); //test函数内部调用func函数不会查询虚函数表
a.test1(); //test1函数内部调用func函数会查询虚函数表
return 0;
}
1.3静的メンバー関数の呼び出し
例えば:
#include<iostream>
using namespace std;
class A
{
public:
int a;
static void func()
{
cout << "静态成员函数" << endl;
}
void func1()
{
cout << "非静态成员函数" << endl;
}
void func2()
{
a = 3;
}
};
int main()
{
A *a = nullptr;
a->func();
//这里有疑问,为什莫a为空也可以调用func函数呢?
//因为,静态成员函数不需要编译器传入this指针,所以ok
a->func1();
//既然a为空指针,为什莫也可以调用非静态成员函数呢?
//因为即使编译器传入了一个this指针,但是在func1中并没有使用this指针,所以即使this指针传入的是空指针也没事
//a->func2(); //执行这行代码程序异常,由于func2中使用this指针为成员变量a赋值,但是this是一个空指针
return 0;
}
2 C ++静的および動的
2.1静的型と動的型
静的型:オブジェクト定義時の型。コンパイル時に決定されます。
動的タイプ:実行時に決定されるオブジェクト参照のタイプ(参照とポリモーフィズムとともに)
例えば:
#include<iostream>
using namespace std;
class A
{
};
class B : public A
{
};
int main()
{
A a; //a的静态类型是A,没有动态类型,因为是直接定义没有使用指针或者引用
B b; //b的静态类型是B,没有动态类型
A* a1; //a1的静态类型是A,目前没有动态类型,因为a1没有指向任何对象
A* a2 = new B(); //a2的静态类型是A,动态类型是B
return 0;
}
2.2静的バインディングと動的バインディング
静的バインディング:バインディングは静的タイプであり、対応する関数またはプロパティはオブジェクトの静的タイプに依存し、コンパイル時に発生します
動的バインディング:バインディングは動的タイプであり、ペアの関数またはプロパティは、実行時に発生するオブジェクトの動的タイプに依存します
2.3非仮想関数の静的バインディング
例えば:
#include<iostream>
using namespace std;
class A
{
public:
void func()
{
cout << "A::func" << endl;
}
};
class B : public A
{
public:
void func()
{
cout << "B::func" << endl;
}
};
int main()
{
B b;
B* b1 = &b;
b1->func(); //func是普通成员函数,发生静态绑定,b1的静态类型是B,所以调用B::func
A* a1 = &b;
a1->func(); //func是普通成员函数,发生静态绑定,b1的静态类型是A,所以调用A::func
return 0;
}
2.4仮想関数の動的バインディング
例えば:
#include<iostream>
using namespace std;
class A
{
public:
virtual void func()
{
cout << "A::func" << endl;
}
};
class B : public A
{
public:
virtual void func()
{
cout << "B::func" << endl;
}
};
int main()
{
B b;
B* b1 = &b;
b1->func(); //func是虚函数,发生动态绑定,b1的动态类型是B,所以调用B::func
A* a1 = &b;
a1->func(); //func是虚函数,发生动态绑定,b1的动态类型是B,所以调用B::func
return 0;
}
2.5仮想関数のデフォルトのパラメーターピット
仮想関数にデフォルトパラメータがある場合、このデフォルトパラメータの値は静的バインディングです。
例えば:
#include<iostream>
using namespace std;
class A
{
public:
virtual void func(int i = 0)
{
cout << "A::func i:" << i << endl;
}
};
class B : public A
{
public:
virtual void func(int i = 1)
{
cout << "B::func i:" << i << endl;
}
};
int main()
{
B b;
B* b1 = &b;
b1->func(); //调用B::func,打印的i的值是1
A* a1 = &b;
a1->func(); //调用B::func,打印的i的值是0(注意)
return 0;
}
3多重継承における仮想関数の探索
3.1仮想基本クラスの継承におけるデストラクタの呼び出し
例えば:
#include<iostream>
using namespace std;
class A
{
public:
~A()
{
cout << "A::~A" << endl;
}
};
class B :public A
{
public:
~B()
{
cout << "B::~B" << endl;
}
};
class A1
{
public:
~A1()
{
cout << "A1::~A1" << endl;
}
};
class B1 :public A1
{
public:
virtual ~B1()
{
cout << "B1::~B1" << endl;
}
};
class A2
{
public:
virtual ~A2()
{
cout << "A2::~A2" << endl;
}
};
class B2 :public A2
{
public:
~B2() //由于继承的A2有一个虚析构函数,所以本类中的析构函数也是虚函数
{
cout << "B2::~B2" << endl;
}
};
int main()
{
//A* a = new B();
//delete a; //因为析构函数不是虚函数,所以发生静态绑定,只会调用A的析构函数,但是a的动态类型是B,此时因为B的this指针和A的this指针指向同一地方,在VS中不会报错,但是没有调用派生类析构函数终究是不严谨的
//A1* a1 = new B1();
//delete a1; //程序在VS中会报错,因为A1的this指针和B1的this指针不指向同一位置
A2* a2 = new B2();
delete a2; //程序正常执行,符合预期,所以在发生有虚函数的继承时,应当给基类添加虚析构函数
return 0;
}
継承では、メモリが正常に解放された場合でも、サブクラスのデストラクタが呼び出されている限り、コンパイラは再帰と同様に、サブクラスのデストラクタで基本クラスのデストラクタを呼び出します。クラスのデストラクタは、レイヤーごとに返される前に呼び出されます
3.2仮想デストラクタなしで2番目の基本クラスによって発生したエラーを解放する
例えば:
#include<iostream>
using namespace std;
class A1{};
class A2{};
class B :public A1, public A2{};
int main()
{
A2 * a2 = new B();
delete a2; //由于返回的是B类实例偏移后的指针,所以不是对象首地址指针,调用delete会发生异常
//解决办法1
//将a2转换为B类
//解决办法2
//给A2类添加一个虚析构函数
return 0;
}