畢業(yè)設(shè)計(jì)網(wǎng)站前端代做關(guān)鍵詞seo優(yōu)化公司
目錄
繼承的概念和定義
繼承的概念
繼承的定義
繼承的定義格式
繼承關(guān)系和訪問限定符?
繼承基類成員訪問方式的變化
訪問權(quán)限實(shí)例
基類和派生類對(duì)象賦值轉(zhuǎn)換
繼承中的作用域
派生類的默認(rèn)成員函數(shù)
繼承與友元
繼承與靜態(tài)成員
復(fù)雜的菱形繼承及菱形虛擬繼承
繼承的概念和定義
繼承的概念
繼承 (inheritance) 機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì) 使代碼可以復(fù)用 的最重要的手段,它允許程序員在 保 持原有類特性的基礎(chǔ)上進(jìn)行擴(kuò)展 ,增加功能,這樣產(chǎn)生新的類,稱派生類。繼承 呈現(xiàn)了面向?qū)ο?/strong> 程序設(shè)計(jì)的層次結(jié)構(gòu) ,體現(xiàn)了由簡(jiǎn)單到復(fù)雜的認(rèn)知過程。以前我們接觸的復(fù)用都是函數(shù)復(fù)用, 繼 承是類設(shè)計(jì)層次的復(fù)用。
?簡(jiǎn)單來說就是類似于在一個(gè)類中包含了另一個(gè)類的成員函數(shù)和成員變量以及對(duì)應(yīng)的訪問權(quán)限。
class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter"; // 姓名int _age = 18;//年齡
};
// 繼承后父類的Person的成員(成員函數(shù)+成員變量)都會(huì)變成子類的一部分。這里體現(xiàn)出了
//Student和Teacher復(fù)用了Person的成員。下面我們使用監(jiān)視窗口查看Student和Teacher對(duì)象,可
//以看到變量的復(fù)用。調(diào)用Print可以看到成員函數(shù)的復(fù)用。
class Student : public Person
{
protected:int _stuid; // 學(xué)號(hào)
};
class Teacher : public Person
{
protected:int _jobid; // 工號(hào)
};
int main()
{Student s;Teacher t;s.Print();t.Print();return 0;
}
?下面是調(diào)試結(jié)果
可以看到子類中繼承的父類的_name和_age,s和t是兩個(gè)不同的類;他們各自繼承的父類的內(nèi)存空間也是不同的,就像每個(gè)孩子繼承父親的家產(chǎn)一樣。
繼承的定義
繼承的定義格式

繼承關(guān)系和訪問限定符?
繼承基類成員訪問方式的變化
總結(jié):
訪問權(quán)限實(shí)例
class Person
{
public:void Print(){cout << _name << endl;}
protected:string _name; // 姓名
private:int _age; // 年齡
};
//class Student : protected Person
//class Student : private Person
class Student : public Person
{void fun(){cout << _stunum << " " << _age << " " << _name; 這里_age顯示無法訪問}
protected:int _stunum; // 學(xué)號(hào)
};int main()
{Student s; s.Print(); return 0;
}
在上述代碼中,Student是以public的形式繼承的父類person,最父類成員函數(shù)、成員變量的訪問權(quán)限依次是public.protected和private。protected的對(duì)象是可以進(jìn)行訪問的。
class Person
{
public:void Print(){cout << _name << endl;}
protected:string _name; // 姓名
private:int _age; // 年齡
};
//class Student : protected Person
//class Student : private Person
class Student : protected Person
{void fun(){cout << _stunum << " " << _age << " " << _name; //顯示不可訪問_name = 6; }
protected:int _stunum; // 學(xué)號(hào)
};int main()
{Student s; s.Print(); //顯示不可訪問 return 0;
}
?在上述代碼中,以protected的形式繼承父類時(shí),對(duì)父類的成員函數(shù)。成員變量的訪問權(quán)限依次是protected.protected和private。print()函數(shù)無法再類外進(jìn)行調(diào)用。
如果以private 的形式繼承父類,那么父類的所有成員的訪問權(quán)限都是private,在子類中都無法進(jìn)行訪問;
基類和派生類對(duì)象賦值轉(zhuǎn)換
派生類對(duì)象 可以賦值給 基類的對(duì)象 / 基類的指針 / 基類的引用 。這里有個(gè)形象的說法叫切片或者切割。寓意把派生類中父類那部分切來賦值過去。基類對(duì)象不能賦值給派生類對(duì)象。基類的指針或者引用可以通過強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對(duì)象時(shí)才是安全的。這里基類如果是多態(tài)類型,可以使用 RTTI(RunTime Type Information) 的 dynamic_cast 來進(jìn)行識(shí)別后進(jìn)行安全轉(zhuǎn)換。
class Person
{
protected:string _name; // 姓名string _sex; ?// 性別int _age; // 年齡
};
class Student : public Person
{
public:int _No; // 學(xué)號(hào)
};
void Test()
{Student sobj;// 1.子類對(duì)象可以賦值給父類對(duì)象/指針/引用Person pobj = sobj;Person* pp = &sobj;Person& rp = sobj;//2.基類對(duì)象不能賦值給派生類對(duì)象sobj = pobj; // 3.基類的指針可以通過強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針pp = &sobj;Student * ps1 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)可以的。ps1->_No = 10; pp = &pobj;Student* ps2 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)雖然可以,但是會(huì)存在越界訪問的問題ps2->_No = 10;
}
繼承中的作用域
// Student的_num和Person的_num構(gòu)成隱藏關(guān)系,可以看出這樣代碼雖然能跑,但是非常容易混淆
class Person
{
protected:string _name = "小李子"; // 姓名int _num = 111; ? // 身份證號(hào)
};
class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " 身份證號(hào):" << Person::_num << endl;cout << " 學(xué)號(hào):" << _num << endl;}
protected:int _num = 999; // 學(xué)號(hào)
};
void Test()
{Student s1;s1.Print();
};
?子類和父類中都有_num但是他們的作用域不同,在子類中只能訪問子類的_num,當(dāng)然也可以通person類訪問父類的_num,
// B中的fun和A中的fun不是構(gòu)成重載,因?yàn)椴皇窃谕蛔饔糜?// B中的fun和A中的fun構(gòu)成隱藏,成員函數(shù)滿足函數(shù)名相同就構(gòu)成隱藏。
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" << i << endl;}
};
void Test()
{B b;b.fun(10);
};
在子類中,只能直接訪問帶參數(shù)的fun,可以通過person::fun()來訪問無參數(shù)的fun。? ? ? ??
派生類的默認(rèn)成員函數(shù)


class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};
class Student : public Person
{
public:Student(const char* name, int num): Person(name), _num(num){cout << "Student()" << endl;}Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator =(s);_num = s._num;}return *this;}~Student(){cout << "~Student()" << endl;}
protected:int _num; //學(xué)號(hào)
};
void Test()
{Student s1("jack", 18);Student s2(s1); Student s3("rose", 17); s1 = s3;
}
其實(shí)可以把父類當(dāng)成一個(gè)自定義類型的成員變量,如果父類有默認(rèn)構(gòu)造函數(shù),那么就是自己自動(dòng)調(diào)用,如果沒有需要手動(dòng)在子類的構(gòu)造函數(shù)的初始化列表中進(jìn)行傳參數(shù)初始化。析構(gòu)函數(shù)會(huì)自動(dòng)調(diào)用??截悩?gòu)造需要再子類的拷貝構(gòu)造函數(shù)中的初始化列表中進(jìn)行調(diào)用。
繼承與友元
class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 學(xué)號(hào)
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
void main()
{Person p;Student s;Display(p, s);
}
在父類中的友元只能訪問父類的成員變量,無法訪問子類的成員變量。
繼承與靜態(tài)成員
class Person
{
public:Person() { ++_count; }
protected:string _name; // 姓名
public:static int _count; // 統(tǒng)計(jì)人的個(gè)數(shù)。
};
int Person::_count = 0;
class Student : public Person
{
protected:int _stuNum; // 學(xué)號(hào)
};
class Graduate : public Student
{
protected:string _seminarCourse; // 研究科目
};
void TestPerson()
{Student s1;Student s2;Student s3;Graduate s4;cout << " 人數(shù) :" << Person::_count << endl;Student::_count = 0;cout << " 人數(shù) :" << Person::_count << endl;
}
所有的實(shí)例化的繼承類都公用這一個(gè)靜態(tài)成員變量。
復(fù)雜的菱形繼承及菱形虛擬繼承




class Person
{
public:string _name; // 姓名
};
class Student : public Person
{
protected:int _num; //學(xué)號(hào)
};
class Teacher : public Person {
protected:int _id; // 職工編號(hào)
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修課程
};
void Test()
{// 這樣會(huì)有二義性無法明確知道訪問的是哪一個(gè)Assistant a;a._name = "peter";// 需要顯示指定訪問哪個(gè)父類的成員可以解決二義性問題,但是數(shù)據(jù)冗余問題無法解決a.Student::_name = "xxx";a.Teacher::_name = "yyy";
}
Teacher和Student類中各有一份person父類,實(shí)際上只需要有一分就可以了,
class Person
{
public:string _name; // 姓名
};
class Student : virtual public Person
{
protected:int _num; //學(xué)號(hào)
};
class Teacher : virtual public Person
{
protected:int _id; // 職工編號(hào)
};
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修課程
};
void Test()
{Assistant a;a._name = "peter";
}
在重復(fù)的父類訪問前加上virtual,進(jìn)行虛擬化,Student和Teacher就會(huì)公用person類了,就解決了繼承的二義性和數(shù)據(jù)冗余的問題。
總結(jié):