再談c++中的多態

何為多態

多態的概念:通俗來說,就是多種形態,具體點就是去完成某個行為,當不同的對象去完成時會產生出不同的狀態。

多態的實現

在繼承的體系下

  1. 基類中必須有虛函數(被virtual關鍵字修飾的成員函數),在派生類中必須要對基類中的虛函數進行重寫
  2. 對于虛函數的調用:必須使用基類的指針或者引用調用虛函數
體現多態性

在代碼運行時,基類指針指向哪個類的對象,就調用哪個類的虛函數

class Person
{
public:Person(const string& name,const string& gender,const int age):_name(name), _gender(gender), _age(age){}void BuyTicket(){cout << "全價票" << endl;}
protected:string _name;string _gender;int _age;
};class Studnet :public Person
{
public:Studnet(const string& name, const string& gender, const int age,const int _stuId):Person(name,gender,age), _stuId(_stuId){}void BuyTicket(){cout << "半價票" << endl;}
protected:int _stuId;};class Soldier:public Person
{
public:Soldier(const string& name, const string& gender, const int age, const string& rank):Person(name,gender,age), _rank(rank){}void BuyTicket(){cout << "免費" << endl;}
protected:string _rank;
};void TestBuyTicket(Person& p)
{p.BuyTicket();
}int main()
{Person p("Tom", "男", 18);Studnet st("小帥", "女", 19,1000);Soldier so("威武", "男", 23, "班長");TestBuyTicket(p);TestBuyTicket(st);TestBuyTicket(so);system("pause");return 0;
}

在這里插入圖片描述
并沒有體現出多態性,如果要讓不同的人買到各自的票,我們可以寫三個重載函數來實現,但是這樣的話,代碼的重復性太高,所以這里就要使用多態。

什么是虛函數

虛函數:就是在類的成員函數的前面加virtual關鍵字

virtual void BuyTicket(){cout << "全價票" << endl;}

什么虛函數的重寫?

虛函數的重寫:**派生類中有一個跟基類的完全相同虛函數,我們就稱子類的虛函數重寫了基類的虛函數,完全相同是指:函數名、參數、返回值都相同。**另外虛函數的重寫也叫作虛函數的覆蓋。
也就是說派生類重寫基類中的某個虛函數—>派生類函數必須要與基類中的虛函數原型完全一致

class Person
{
public:virtual void BuyTicket(){cout << "全價票" << endl;}
};
class Studnet :public Person
{
public:virtual void BuyTicket(){cout << "半價票" << endl;}
};

注意事項

  1. 基類中與子類中的同名方法不是虛函數,但是子類中是虛函數,不是多態,是繼承中的重定義,同名隱藏。基類同名成員函數前必須加virtual關鍵字
  2. 子類中把虛函數關鍵字去掉,但是此函數在基類中是虛函數,此時是多態。所以子類同名成員函數前virtual關鍵字是否添加都可以
  3. 基類虛函數必須要與派生類虛函數的原型完全相同(返回值類型,函數名字(參數列表))
class Base
{
public:virtual void TestFunc1(){cout << "Base::TestFunc1()" << endl;}virtual void TestFunc2(int ){cout << "Base::TestFunc2()" << endl;}void TestFunc3(){cout << "Base::TestFunc3()" << endl;}virtual void TestFunc4(){cout << "Base::TestFunc4()" << endl;}
};class Derived :public Base
{
public:virtual void TestFunc1(){cout << "Derived::Testfun1()" << endl;}virtual void TestFunc2(){cout << "Derived::TestFunc2()" << endl;}virtual void TestFunc3(){cout << "Derived::TestFunc3()" << endl;}void TestFunc4(){cout << "Derived::TestFunc4()" << endl;}
};void TestVirtualFunc(Base* pb)
{pb->TestFunc1();pb->TestFunc2(10);pb->TestFunc3();pb->TestFunc4(); 
}
int main()
{Base b;Derived d;TestVirtualFunc(&b);TestVirtualFunc(&d);system("pause");return 0;
}

在這里插入圖片描述
我們定義一個函數,形參為基類的指針,然后在主函數中分別創建基類對象,子類對象,然后將他們傳進去,第一個傳的是基類對象,所以是基類的指針指向了基類的對象,所以調的全部都是基類的函數。第二個傳的是子類的對象,就是基類指針指向子類對象,但是因為Func2函數與基類中的原型不一致,Func3()函數基類中沒有加virtual關鍵字,所以這兩個函數并沒有實現多態,都調用的是基類的函數,而1和4調用的是子類的函數,實現了多態。

虛函數重寫的例外:協變

基類中虛函數**返回基類(基類1)**的引用(指針),子類的虛函數返回子類1(只要繼承于基類1)的引用(指針)基類和子類虛函數的返回值類型不同

class A{};//基類
class B : public A {}; //子類
class Person {//基類1
public:virtual A* f() {return new A;}//返回值的是基類,不是基類1
};
class Student : public Person {//子類1繼承于基類1
public:virtual B* f() {return new B;}//返回值是子類,不是子類1的
};

虛函數重寫的例外:虛函數

函數名字不同,但是可以構成重寫。編譯器對析構函數的名稱做了特殊處理,編譯后
析構函數的名稱統一處理成destructor,這也說明的基類的析構函數最好寫成虛函數。

class Person {
public:virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生類Student的析構函數重寫了Person的析構函數,下面的delete對象調用析構函數,才能構成多
//態,才能保證p1和p2指向的對象正確的調用析構函數。
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}

針對于上面的代碼,如果基類和子類都不寫成虛函數。
在這里插入圖片描述
基類不是虛函數,但是子類是。程序會出內存泄露
在這里插入圖片描述
正常的結果
在這里插入圖片描述

接口繼承和實現繼承

普通函數的繼承是一種實現繼承,派生類繼承了基類函數,可以使用函數,繼承的是函數的實現。虛函數的繼承是一種接口繼承,派生類繼承的是基類虛函數的接口,目的是為了重寫,達成多態,繼承的是接口。所以如果不實現多態,不要把函數定義成虛函數。

重載,重寫(覆蓋),重定義(隱藏)

重載重寫(覆蓋)重定義(隱藏)
兩個函數在同一作用域兩個函數分別在基類和派生類的作用域兩個函數分別在基類和派生類的作用域
函數名/參數相同函數名/參數/返回值都必須是相同的(協變例外)函數名相同
兩個函數必須是虛函數兩個基類和派生類的同名函數不構成重寫就是重定義

按值傳參與按指針/引用傳參的區別

void TestBuyTicket(Person *p)
{p->~Person();
}
  • 在編譯階段,編譯器無法確認基類的指針到底指向哪個類的對象,因為函數在執行期間才會傳參,因此在編譯期間無法確認虛函數的行為。只能在代碼運行時,才可以確定該基類指針指向哪個類的對象。
void TestBuyTicket(Person p)
{p.~Person();//在調用期間,不論傳遞哪個類的對象,該函數調用的都是基類的
}
  • 編譯期間,因為該函數按照值的方式傳參,參數已經確認。因此在編譯階段,就會生成基類的臨時對象,因此該函數在編譯期間可以確定虛函數行為,已經確定調用哪個類的函數。

C++11 override 和 final

override:只能修飾派生類的虛函數
作用:檢測派生類中的某個虛函數是否重寫了哪個虛函數,防止函數名有時候寫錯,沒有構成重寫。
final:可以修飾類—表示該類不能被繼承,修飾虛函數—虛函數不能被繼承

// 1.final 修飾基類的虛函數不能被派生類重寫
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() {cout << "Benz-舒適" << endl;}
};

抽象類

在虛函數的后面寫上 =0 ,則這個函數為純虛函數。包含純虛函數的類叫做抽象類(也叫接口類),抽象類不能實例化出對象。派生類繼承后也不能實例化出對象,只有重寫純虛函數,派生類才能實例化出對象。純虛函數規范了派生類必須重寫,另外純虛函數更體現出了接口繼承

class WC
{
public:void GoToManRoom(){cout << "go to left"<<endl;}void GoToWoManRoom() {cout << "go to right"<<endl;}
};
//作用:規范后續的接口
class Person
{//不能實例化對象,但可以創建該類的指針
public://純虛函數virtual void GoToWc(WC& wc) = 0;string _name;int _age;
};class Man :public Person
{
public:void GoToWc( WC& wc){wc.GoToManRoom();}
};
class WoMan :public Person
{
public:void GoToWc(WC& wc){wc.GoToWoManRoom();}
};
#include<Windows.h>
#include<time.h>
//Monster 也是抽象類,因為該類沒有重寫基類中的純虛函數
class Monster :public Person
{};
void TestWC(int n)
{WC wc;srand(time(nullptr));for (int i = 0; i < n; ++i){Person* pGuard;if (rand() % 2 == 0)pGuard = new Man;elsepGuard = new WoMan;pGuard->GoToWc(wc);delete pGuard;Sleep(1000);}
}
int main()
{//Person* p;TestWC(10);system("pause");return 0;
}

在這里插入圖片描述

多態的原理

無繼承

class Base
{
public:Base(){}virtual void TestFunc3(){cout << "Base::TestFunc2()" << endl;}virtual void TestFunc1(){cout << "Base::TestFunc1()" << endl;}virtual void TestFunc2(){cout << "Base::TestFunc2()" << endl;}int _b;
};
  • 一個類中包含有虛函數,會給該類的對象多增加四個字節,該4字節中的內容是在構造函數中填充的。
  • 如果類沒有顯示定義構造函數,編譯器會給該類生成一個默認的構造函數,作用:給對象的前4個字節賦值
  • 如果類顯示定義了自己的構造函數,編譯器將會對構造函數進行修改,多增加一條語句,給對象的前4個字節賦值

在這里插入圖片描述

出現繼承

class Base
{
public:Base(){}virtual void TestFunc3(){cout << "Base::TestFunc2()" << endl;}virtual void TestFunc1(){cout << "Base::TestFunc1()" << endl;}virtual void TestFunc2(){cout << "Base::TestFunc2()" << endl;}int _b;
};
class Derived :public Base
{};

虛表指針不一樣,派生類和基類用的不是同一張虛表
在這里插入圖片描述

基類虛表構建過程

將虛函數按照其在類中的聲明次序依次放到虛表中。

class Base
{
public:Base(){}virtual void TestFunc3(){cout << "Base::TestFunc2()" << endl;}virtual void TestFunc1(){cout << "Base::TestFunc1()" << endl;}virtual void TestFunc2(){cout << "Base::TestFunc2()" << endl;}int _b;
};
class Derived :public Base
{
public:virtual void TestFunc1(){cout << "Derived::TestFunc1()" << endl;}virtual void TestFunc3(){cout << "Derived::TestFunc2()" << endl;}int _d;
};

派生類虛表的構建過程

  1. 將基類虛表中內容拷貝一份放到子類的虛表中
  2. 如果派生類重寫了基類的某個虛函數,用派生類自己的虛函數地址替換虛表中相同偏移量位置的基類虛函數。
  3. 派生類自己新增加的虛函數按其在派生類中的聲明次序增加到派生類虛表的最后。
    在這里插入圖片描述

調用原理

class Base
{
public:Base(){}virtual void TestFunc1(){cout << "Base::TestFunc1()" << endl;}virtual void TestFunc2(){cout << "Base::TestFunc2()" << endl;}virtual void TestFunc3(){cout << "Base::TestFunc3()" << endl;}void TestFunc4(){cout << "Base::TestFunc4()" << endl;}int _b;
};
class Derived :public Base
{
public:virtual void TestFunc1(){cout << "Derived::TestFunc1()" << endl;}virtual void TestFunc2(){cout << "Base::TestFunc2()" << endl;}virtual void TestFunc3(){cout << "Derived::TestFunc3()" << endl;}int _d;
};
//虛函數的調用:通過基類的指針或者引用調用虛函數
void TestVirtual(Base *pb)
{pb->TestFunc1();pb->TestFunc2();pb->TestFunc3();pb->TestFunc4();
}
01104BC3  mov         eax,dword ptr [pb]  
01104BC6  mov         edx,dword ptr [eax]  //前兩步,從對象前4個字節取虛表的地址
01104BC8  mov         esi,esp  
01104BCA  mov         ecx,dword ptr [pb]  	//傳遞this指針
01104BCD  mov         eax,dword ptr [edx+4]  //從虛表中找對應的虛函數
01104BD0  call        eax  //調用虛函數
//重新解析d對象的內存空間,
//將d對象的內存空間按照基類對象方式進行解析
//這個過程并沒有創建新的對象,所以還是調用派生類的函數
Base* pb = (Base*)&d;
pb->TestFunc1();
return 0;

打印虛函數表

單繼承

class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a;
};
class Derive :public Base {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }virtual void func4() { cout << "Derive::func4" << endl; }
private:int b;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{// 依次取虛表中的虛函數指針打印并調用。調用就可以看出存的是哪個函數cout << " 虛表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d個虛函數地址 :0X%x,->", i, vTable[i]);VFPTR f = vTable[i];f();//調用虛函數,f=vTable[i] vTable[i]等于一個虛函數入口地址}cout << endl;
}
int main()
{Base b;Derive d;
// 思路:取出b、d對象的頭4比特,就是虛表的指針,虛函數表本質是一個存虛函數指針的指針數組,這個數組最后面放了一個nullptr
// 1.先取b的地址,強轉成一個int*的指針
// 2.再解引用取值,就取到了b對象頭4比特的值,這個值就是指向虛表的指針
// 3.再強轉成VFPTR*,因為虛表就是一個存VFPTR類型(虛函數指針類型)的數組。
// 4.虛表指針傳遞給PrintVTable進行打印虛表
// 5.需要說明的是這個打印虛表的代碼經常會崩潰,因為編譯器有時對虛表的處理不干凈,虛表最后面沒有
//放nullptr,導致越界,這是編譯器的問題。我們只需要點目錄欄的 - 生成 - 清理解決方案,再編譯就好了。VFPTR* vTableb = (VFPTR*)(*(int*)&b);PrintVTable(vTableb);VFPTR* vTabled = (VFPTR*)(*(int*)&d);PrintVTable(vTabled);system("pause");return 0;
}

在這里插入圖片描述

多繼承

class Base1 {
public:virtual void func1() { cout << "Base1::func1" << endl; }virtual void func2() { cout << "Base1::func2" << endl; }
private:int b1;
};
class Base2 {
public:virtual void func1() { cout << "Base2::func1" << endl; }virtual void func2() { cout << "Base2::func2" << endl; }
private:int b2;
};
class Derive : public Base1, public Base2 {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{cout << " 虛表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d個虛函數地址 :0X%x,->", i, vTable[i]);VFPTR f = vTable[i];f();}cout << endl;
}
int main()
{Derive d;VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);PrintVTable(vTableb1);VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));PrintVTable(vTableb2);system("pause");return 0;
}

在這里插入圖片描述
在這里插入圖片描述
多繼承派生類虛表的構建過程

  1. 與單繼承中派生類構建過程相同的
  2. 對于派生類新增加的虛函數按照其在類中的聲明次序增加到第一個虛表的最后。

總結

  1. 同一個類的不同對象,在底層共享一張虛表
  2. 派生類除了繼承基類的東西,如果自己沒有加任何的新東西,雖然派生類的虛表內容和基類的內容是一樣的,但是不是同一張虛表,因為虛表指針不一樣。派生類不會和基類共用同一張虛表派生類也有自己的虛表。
  3. 基類b對象和派生類d對象虛表是不一樣的,這里我們發現Func1完成了重寫,所以d的虛表中存的是重寫的Derive::Func1,所以虛函數的重寫也叫作覆蓋,覆蓋就是指虛表中虛函數的覆蓋。重寫是語法的叫法,覆蓋是原理層的叫法。
  4. 虛函數表本質是一個存虛函數指針的指針數組,這個數組最后面放了一個nullptr。
  5. 虛表存的是虛函數指針,不是虛函數,虛函數和普通函數一樣的,都是存在代碼段的,只是他的指針又存到了虛表中。另外對象中存的不是虛表,存的是虛表指針。那么虛表存在哪的呢?vs下是存在代碼段

問題

什么是多態?

概念

同一個事物在不同場景下可以表現出的多種形態,例如迪迦奧特曼的三種形態。

多態的分類

靜態多態(靜態綁定/早綁定)動態多態(動態綁定/晚綁定)
在編譯時,可確定具體哪個函數在編譯階段無法確定函數具體調用哪個函數,必須在代碼運行時才能確定,無法確定基類指針或者引用到底指向哪個類的對象
函數重載/模板虛函數+繼承

多態的實現條件

  1. 必須在繼承體系中
  2. 在基類中必須要有虛函數(被virtual關鍵字修飾的成員函數),派生類必須對基類的虛函數進行重寫
  3. 關于虛函數的調用:必須通過基類的指針或者引用調用虛函數

inline函數可以是虛函數嗎?

不能,因為inline函數沒有地址,無法把地址放到虛函數表中。inline在編譯時期展開,多態發生在運行時。

靜態成員可以是虛函數嗎?

不能,因為靜態成員函數沒有this指針,使用類型::成員函數的調用方式無法訪問虛函數表,所以靜態成員函數無法放進虛函數表。

構造函數可以是虛函數嗎?

不能,因為對象中的虛函數表指針是在構造函數初始化列表階段才初始化的。
構造函數的作用:

  1. 初始化類中的成員變量
  2. 將虛表指針放在對象的前4個字節---->編譯器自己完成。

析構函數可以是虛函數嗎?什么場景下析構函數是虛函數?

可以,并且最好把基類的析構函數定義成虛函數。析構函數可以為虛函數---->重寫的一種特例,因為派生類重寫基類中的虛析構函數,名字不一樣。

class Base
{
public:Base(int b):_b(b){cout << "Base::Base()" << endl;}virtual ~Base(){cout << "Base::~Base()" << endl;}int _b;
};
class Derived :public Base
{
public:Derived(int b):Base(b){_p = new int[10];}~Derived(){delete[]_p;}
private:int *_p;
};
int main()
{//靜態類型:聲明變量時的類型----在編譯期間起作用//動態類型:實際引用(指向)的類型----在運行時確定調用哪個類的虛函數Base* pb = new Derived(10);delete pb;//看析構函數是不是虛函數,如果不是用靜態類型,//delete:1.調用析構函數釋放對象中的資源//		 2.調用operator delete()釋放對象的空間system("pause");return 0;
}

如果派生類中涉及到動態資源的管理(比如:子類從堆上申請空間),建議:基類中的析構函數最好設置為虛函數,否則可能存在內存泄露

對象訪問普通函數快還是虛函數更快?

首先如果是普通對象,是一樣快的。如果是指針對象或者是引用對象,則調用的普通函數快,因為構成多態,運行時調用虛函數需要到虛函數表中去查找。

普通函數(指非虛函數)調用虛函數調用(沒有完全實現多態條件)虛函數調用(多態實現條件完全滿足)
傳參(如果有參數)跟普通函數調用一樣從對象的前4個字節中取虛表地址
通過指令call調用該函數(call 函數入口地址)傳參(包括this指針)
從虛表中獲取函數地址
調用函數

多態的缺陷:降低程序運行的速度

虛函數表是在什么階段生成的,存在哪的?

虛函數是在編譯階段就生成的,一般情況下存在代碼段(常量區)的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383125.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383125.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383125.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

再談c++中的繼承

繼承的概念 繼承(inheritance)機制是面向對象程序設計使代碼可以復用的最重要的手段&#xff0c;它允許程序員在保持原有類特性的基礎上進行擴展&#xff0c;增加功能&#xff0c;這樣產生新的類&#xff0c;稱派生類。繼承呈現了面向對象程序設計的層次結構&#xff0c;體現了…

紅黑樹概念及其相關操作的實現

紅黑樹的概念 紅黑樹&#xff0c;是一種二叉搜索樹&#xff0c;但它并不像AVL樹一樣&#xff0c;每個結點綁定一個平衡因子。但在每個結點上增加一個存儲位表示結點的顏色&#xff0c;可以是Red或Black。 通過 對任何一條從根到葉子的路徑上各個結點著色方式的限制&#xff0c…

模擬實現STL中map和set容器

紅黑樹的迭代器 //紅黑樹的迭代器 template<class T> struct RBTreeIterator {typedef RBTreeNode<T>Node;typedef RBTreeIterator<T> Self; public:RBTreeIterator(Node* pNode nullptr):_pNode(pNode){}//具有指針操作T& operator*(){return _pNode-…

【劍指offer】_05 連續子數組最大和

題目描述 HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會后,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,并期望旁邊的正數會彌補它呢&#…

排序上---(排序概念,常見排序算法,直接插入,希爾排序,直接選擇排序,堆排序)

排序的概念 排序&#xff1a;所謂排序&#xff0c;就是使一串記錄&#xff0c;按照其中的某個或某些關鍵字的大小&#xff0c;遞增或遞減的排列起來的操作。穩定性&#xff1a;假定在待排序的記錄序列中&#xff0c;存在多個具有相同的關鍵字的記錄&#xff0c;若經過排序&…

排序下---(冒泡排序,快速排序,快速排序優化,快速排序非遞歸,歸并排序,計數排序)

排序上 排序上 交換類排序 基本思想&#xff1a;所謂交換&#xff0c;就是根據序列中兩個記錄鍵值的比較結果來對換這兩個記錄在序列中的位置&#xff0c;交換排序的特點是&#xff1a;將鍵值較大的記錄向序列的尾部移動&#xff0c;鍵值較小的記錄向序列的前部移動。 冒泡…

哈希的概念及其操作

哈希概念 順序結構以及平衡樹中&#xff0c;元素關鍵碼與其存儲位置之間沒有對應的關系&#xff0c;因此在查找一個元素時&#xff0c;必須要經過關鍵碼的多次比較。順序查找時間復雜度為O(N)&#xff0c;平衡樹中為樹的高度&#xff0c;即O( Log2N)&#xff0c;搜索的效率取決…

軟件工程---1.概述

軟件的特征 抽象&#xff1a; 不可觸摸&#xff0c;邏輯實體&#xff0c;可記錄&#xff0c;但看不到復制成本低&#xff1a;不受物質材料的限制&#xff0c;不受物理定律或加工過程的制約&#xff0c;與開發成本相比&#xff0c;復制成本很低無折舊、受硬件制約、未完全擺脫手…

軟件工程---2.軟件過程

三個模型 瀑布模型增量模型集成和配置模型 沒有適用于所有不同類型軟件開發的過程模型。 瀑布模型 需求定義系統和軟件的設計實現與單元測試集成與系統測試運行與維護 瀑布模型的特征 從上一項活動中接受該項活動的工作成果&#xff08;工作產品&#xff09;&#xff0c;作…

軟件工程---3.敏捷軟件開發

敏捷軟件開發 極限編程&#xff08;XP&#xff0c; Beck1999&#xff09;Scrum方法&#xff08;Schwaber and Beedle 2001&#xff09;DSDM方法&#xff08;Stapleton 2003&#xff09; 敏捷軟件的開發宣言 個體和交互勝過過程和工具可以工作的軟件勝過面面俱到的文檔客戶合…

軟件工程---4.需求工程

需求工程定義 找出、分析、文檔化并且檢查需求的過程被稱為需求工程 需求的兩個描述層次 用戶需求&#xff0c;指高層的抽象需求。使用自然語言、圖形描述需求。系統需求&#xff0c;指底層的詳細需求。使用系統需求文檔&#xff08;有時被稱為功能規格說明&#xff09;應該…

軟件工程---5.系統建模

從不同視角對系統建模 外部視角&#xff0c;上下文模型&#xff0c;對系統上下文或環境建模交互視角&#xff0c;交互模型&#xff08;功能模型&#xff09;&#xff0c;對系統與參與者或系統內構件之間的交互建模結構視角&#xff0c;結構模型&#xff08;靜態模型&#xff0…

軟件工程---6.體系結構設計

體系結構模型是什么&#xff1f; 體系結構模型&#xff0c;該模型描述系統如何被組織為一組相互通信的構件 體系結構分類 小體系結構關注單個程序的體系結構。在這個層次上&#xff0c;我們關注單個的程序是如何補分解為構件的。大體系結構關注包括其他系統、程序和程序構件…

【劍指offer】_06 變態跳臺階

題目描述 一只青蛙一次可以跳上1級臺階&#xff0c;也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。 解題思路 鏈接&#xff1a;https://www.nowcoder.com/questionTerminal/22243d016f6b47f2a6928b4313c85387 關于本題&#xff0c;前提是…

【劍指offer】_07 矩形覆蓋

題目描述 我們可以用21的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個21的小矩形無重疊地覆蓋一個2*n的大矩形&#xff0c;總共有多少種方法&#xff1f; 解題思路 依舊是斐波那契數列 2n的大矩形&#xff0c;和n個21的小矩形 其中target*2為大矩陣的大小 有以下幾種情形…

軟件工程---07.設計與實現

軟件設計和軟件實現 軟件設計是一個創造性的活動&#xff0c;在此活動中需要基于客戶需求識別軟件構件及其關系。軟件實現是將設計實現為一個程序的過程 為開發一個系統設計&#xff0c;你需要 理解并定義上下文模型以及系統的外部交互設計系統體系結構識別系統中的主要對象…

軟件工程---08.軟件測試

測試 測試的正向思維&#xff08;確認測試&#xff09; 向開發人員和客戶展示軟件滿足其需求測試的逆向思維&#xff08;缺陷測試&#xff09;找出可能導致軟件行為不正確原因。測試是更廣闊的軟件確認和驗證( Verification and Validation; V & V)過程的一部分。驗證和確…

軟件工程---15.軟件復用

復用的圖(牢記) 軟件復用的好處 開發加速有效的專家利用提高可依賴性降低開發成本降低過程風險符合標準 軟件復用的缺點 創建&#xff0c;維護以及使用一個構件庫查找&#xff0c;理解以及適配可復用構件維護成本增加缺少工具支持“不是在這里發明的”綜合癥 應用框架 現在…

軟件工程---16.基于構件的軟件工程

CBSE CBSE是定義、實現、集成或組裝松散耦合的獨立構件成為系統的過程。 基于構件的軟件工程的要素有: 完全由接口進行規格說明的獨立構件。構件標準使構件集成變得更為容易。中間件為構件集成提供軟件支持。開發過程適合基于構件的軟件工程。 CBSE的設計原則 構件是獨立的…

軟件工程---17.分布式軟件工程

分布式系統的5個優點 資源共享開放性并發性可伸縮性容錯性 分布式計算中必須考慮的設計問題 透明性&#xff1a;隱藏底層分布 開放性 可伸縮性 三個維度 規模&#xff1a;又分為增強擴展(單挑)&#xff0c;增加擴展(群毆)分布可靠性 信息安全性 主要防止以下類型的攻擊 攔…