C++ 繼承與派生 派生類與基類之間的轉換關係,函數運算符調用關係
一.派生類轉換爲基類
總結:在轉換的過程中,基類總是在左邊,派生類總是在右邊,相當於是一次向上轉型
有三種轉換形式:
- 基類指針指向new出的派生類
- 基類引用派生類
- 基類對象 = 派生類對象 ,利用賦值運算符
用程序來理解:
#include <iostream>
using std::cout;
using std::endl;
//基類與派生類之間的相互轉換
/*
總結:在轉換的過程中,基類總是在左邊,派生類總是在右邊,相當於是一次向上轉型
*/
class Point
{
public:
explicit Point(int x, int y) : _x(x), _y(y)
{
}
void print() const
{
cout << "(" << _x << "," << _y << ")" << endl;
}
int getX() const { return _x; }
int getY() const { return _y; }
private:
int _x;
int _y;
};
class Point3D : public Point
{
public:
Point3D(int x, int y, int z) : Point(x, y), _z(z)
{
}
void print() const
{
cout << "(" << getX() << "," << getY() << "," << _z << ")" << endl;
}
private:
int _z;
};
//1.派生類對象賦值給基類對象
void test1()
{
Point p1(1, 2);
Point3D p2(3, 4, 5);
p1.print();
p2.print();
p1 = p2; //派生類對象可以向基類對象賦值
//p2 = p1;//反之不行
p1.print();
}
//2.派生類對象賦值給基類的引用
void test2()
{
Point3D p2(3, 4, 5);
p2.print();
Point &p1 = p2;
p1.print();
}
//3.基類指針指向派生類對象
void test3(){
Point3D p1(1,2,3);
Point *p2 = &p1;
p2->print();
}
int main()
{
// test3();
return 0;
}
二.派生類與基類:調用函數與運算符
派生類的
1.構造函數執行過程
2.拷貝構造函數執行過程
3.=運算符執行過程
4.<< 運算符執行過程
首先來看這個主程序:
class Base
{
public:
Base(const char *str)
: _pstr(new char[strlen(str) + 1])
{
strcpy(_pstr, str);
cout << "Base(const char* str)" << endl;
}
Base(const Base &lhs)
: _pstr(new char[strlen(lhs._pstr) + 1])
{
strcpy(_pstr, lhs._pstr);
cout << "Base(const Base& lhs)" << endl;
}
Base &operator=(const Base &lhs)
{
if (this != &lhs)
{
delete[] _pstr;
_pstr = new char[strlen(lhs._pstr) + 1];
strcpy(_pstr, lhs._pstr);
cout << "Base& operator=(const Base&)" << endl;
}
return *this;
}
~Base()
{
if (_pstr)
{
cout << "~Base()" << endl;
delete[] _pstr;
_pstr = NULL;
}
}
friend ostream &operator<<(ostream &, const Base &);
private:
char *_pstr;
};
ostream &operator<<(ostream &os, const Base &lhs)
{
os << lhs._pstr;
return os;
}
class Derived : public Base
{
public:
Derived(const char *str1, const char *str2)
: Base(str1), _pstr(new char[strlen(str2) + 1])
{
strcpy(_pstr, str2);
cout << "Derived(const char*, const char*)" << endl;
}
Derived(const char *str)
: _pstr(new char[strlen(str) + 1]), Base(str)
{
strcpy(_pstr, str);
cout << "Derived(const char* str)" << endl;
}
Derived(const Derived &lhs)
: _pstr(new char[strlen(lhs._pstr) + 1]), Base(lhs) //這裏的Derived類型會自動轉換爲Base類型,再調用Base的拷貝構造函數
{
strcpy(_pstr, lhs._pstr);
cout << "Derived(const Derived& lhs)" << endl;
}
Derived &operator=(const Derived &lhs)
{
if (this != &lhs)
{
delete[] _pstr;
_pstr = new char[strlen(lhs._pstr) + 1];
strcpy(_pstr, lhs._pstr);
#if 0
Base& b = static_cast<Base&>(*this);
b = static_cast<Base>(lhs);
#endif
Base::operator=(lhs);
cout << "Derived& operator=(const Derived&)" << endl;
}
return *this;
}
~Derived()
{
if (_pstr)
{
cout << "~Derived()" << endl;
delete[] _pstr;
}
}
friend ostream &operator<<(ostream &, const Derived &);
private:
char *_pstr;
};
ostream &operator<<(ostream &os, const Derived &lhs)
{
const Base &b = static_cast<const Base &>(lhs);
os << b << "," << lhs._pstr;
return os;
}
接下來是測試程序
1.基類(派生類) 調用拷貝構造函數:
在執行Base b(d)的過程中:
d傳入參數中,強制轉換爲Base類,在調用Base類的拷貝構造函數
注意:派生類(基類) 這樣調用拷貝構造函數是非法的,除非重定義
void test0(){
Derived d("Base", "Derived");
Base b(d);
cout << b << endl;
cout << d << endl;
}
2.基類 = 派生類 調用=運算符
在執行強制類型轉換的時候:
1).首先調用Base類的拷貝構造函數,將d強制轉換後成Base類之後進行拷貝構造;
2).在用Base類中的賦值運算符,將其賦值給b
3).臨時拷貝構造的類進行析構
注意:
1).如果不使用static_cast的話則不會調用拷貝構造函數,但是並不安全!
2).派生類 = 基類 這樣轉換是非法的,除非重定義!
void test1()
{
Base b("Base");
Derived d("Derived_base1", "derived");
b = static_cast<Base>(d);
cout << b << endl;
}
3.派生類 = 派生類 :
需要在派生類中重載運算符 =
注意:如果不在Derived類中重載=運算符的話,編譯器只會自動調用Base類的=運算符,但是Derived類中的函數則是會讓_pstr改變指向而已,並不會新開闢空間,從而產生錯誤!(類似於淺拷貝)
void test2(){
Derived *d1 = new Derived("base1", "derived1");
Derived *d2 = new Derived("base2", "derived2");
*d2 = *d1;
delete d1;
d1 = NULL;
//不在Derived中重載=的話
cout << *d2 << endl;//base1,葺葺葺葺葺葺
}