c++面向物件程式設計總結(類的使用)
本篇算是學習c++有關類的知識的一些易錯點吧.....
並不是特別詳細
幾點並不關於類的東西
1.函式模板,用虛擬型別來實現模板的功能
#include<iostream> using namespace std; template <typename t>//t為虛擬型別的名字,自己起的 t maxx(t a,t b,t c) { return max(a,max(b,c)); } int main() { double a=1.1,b=2.2,c=3.3;//不管為double 還是int都可以呼叫maxx cout<<maxx(a,b,c)<<endl;int a1=1,b1=2,c1=3; cout<<maxx(a1,b1,c1)<<endl; }
2.關於函式預設的引數值要放到最右邊
void f1(float a,int b=0,int c,char d='a');//錯誤, void f2(float a,int c,int b=0,char d='a');//正確
3.內建函式
函式最左邊加上inline(我覺得沒啥用),規模很小的函式才用
4.字串
sizeof(string) 為4,因為系統分配的是固定的位元組數,存放的是字串的地址
.......(以後再補充把)
開始類的學習
1.三種類的型別
public
這個就不多說了,類的對外介面
private
想要訪問只能通過該類中的函式來訪問
protected
和private差不多,區別在於繼承時,以後說
2.類的宣告和成員函式的分離(以後更新)
3.建構函式
沒有返回值,名字和類名字一樣#include<iostream>
using namespace std; class box{ public: box(int ,int ,int );//建構函式 (有無預設引數都行) int volume(); private: int h; intw; int l; }; box::box(int a,int b,int c) { h=a,w=b,l=c; } //其實一般這樣寫 // box::box(int a,int b,int c):h(a),w(b),l(c){}//注意如果是陣列的話 則要寫在大括號內//box::box(int a,int b,int c,char nam[]):h(a),w(b),l(c)//{strcpy(name,nam);}
可以用另一個物件初始化另一個
time t1;
time t2=t1; //注意是吧t1的資料成員複製到t2,而不呼叫t2的建構函式
4.解構函式
注意一點,先構造的後析構,相當於棧,先進後出
靜態區域性物件,在函式結束時,並不釋放,也就不呼叫解構函式
5.物件陣列
box b[3] = (1 , 2 ,3)//這樣其實不對,這三個實參則分別作為3個元素的第一個實參
初始化應該
box a[3]={ box(10,20,30); box(20,30,40); box(1,2,3); }
6.物件指標
先說下函式指標。。。。還有函式指標????
型別名(* 指標變數名)(引數列表)
void (* p)();//p是一個指向void型函式的指標 p=fun;//fun函式入口地址付給p 注意沒有括號 (*p)();
物件成員函式有些複雜
要求 函式引數型別和個數匹配 函式返回值型別一樣 所屬的類一樣
void ( time:: *p )();//此時p為指向time類中的成員函式指標
time t; void (time:: *p)(); p = &time::gettime(); (t.*p)();
7.this指標(指向當前物件)
當前被呼叫的成員函式所在物件的起始地址
int box::volume() {return (h*l*w);}//實際為{ return this->h * this->l * this->w;}
呼叫時 如 a.volume() ,實際為將物件a的地址傳給形參this指標
8.常物件只能通過建構函式引數表來對其初始化,所有資料成員絕對不能被改變,並且只能呼叫它的常成員函式 如果非要改變,要加上 mutable 如有一個計數變數count, 則要 mutable int count;
非const資料成員 非const函式可引用和改變 const函式可引用不可改變const資料成員 非const函式可引用不可改變 const函式可引用不可改變const函式不可呼叫非const函式
常指標 如 Time t1;Time * const p = =&t1;p不可再改變常變數只能被常指標指向,,普通變數也可被常指標指向,但這時該普通變數就在這期間變成的常變數,不能改變
複製建構函式Box box2(box);
9.靜態資料成員
資料宣告前 加 static特點是可以被每個該同類物件所引用,只能在類體外進行初始化,在類外也可直接引用 如 int Box::height = 10;//不必加static可以通過物件名來引用,也可以通過類名如 cout<<a.count<<endl;cout<<Box::count<<endl; 10.友元 友元函式可以使一般的,也可以是另一個類中的,可以訪問私有資料成員 友元類就是全家都是友元函式注意是單向的,注意不能傳遞
11.類的模板
temple<class t>//t 為虛擬變數名字 可以有多個,但都要加class 如:temple<class t1,class t2> class compare{ public: compare(t a,t b) { x=a,y=b; } t max() { return max(a,b); } private: t x,y; };
定義物件時為:compare<int> cmp(3,4);//多個時 compare<int ,double> cmp(3,4);
12.對運算子的過載
class yuan{ public: yuan(double a,double b):x(a),y(b){}; yuan operator +(yuan &t) { return yuan(x+t.x, y+t.y); } private: double x,y; };
此時如果有 yuan c1(1,2),c2(1,2),c3;c3 = c1 + c2; 則實際為 c3 = c1.operator(c2);
但其實我覺得更方便的是通過友元函式
class yuan{ public: yuan(double a,double b):x(a),y(b){}; friend yuan operator +(yuan &t1,yuan &t2)//這個其實挺靈活的,可以自行改變 { return yuan(t1.x+t2.x, t1.y+t2.y); } private: double x,y; };
c3 = c1 + c2 則解釋為operator +(c1,c2);
13.繼承
派生類擁有基類的資料成員,其分配如下
先說公有繼承
基類屬性 派生類
private 不可訪問
public 公有繼承後 public
protected protected
私有繼承
基類屬性 派生類
private 不可訪問
public 私有繼承後 private
protected private
保護繼承
保護成員:只有子女(派生類)可以訪問,(友元函式也不行)
基類屬性 派生類
private 不可訪問
public 保護繼承後 protected
protected protected
14.有子物件的派生建構函式
#include<iostream> using namespace std; class Student{ public: void display(); Student(int n,string nam):num(n),name(nam){} protected: int num; string name; }; class Student1: public Student{ public: Student1(int n,string nam,int n1,string nam1,int a,string ad): Student(n,nam),monitor(n1,nam1),age(a),addr(ad){}//注意初始化,一般用初始化表來 ,同樣的,在多級派生中也是如此來構造 void show() { monitor.display(); } protected: Student monitor;//派生類中的子物件 int age; string addr; }; int main() { }
多級的形式
派生類構造名: 基類1建構函式(引數表) , 基類2建構函式(引數表) , 基類3建構函式(引數表)
{ 派生類中新增的資料成員初始化語句 }
15 . 關於多重繼承的二義性問題
就是繼承的函式名 和 派生的函式名一樣了
假設有類A和類B,此時類C同時繼承類A和類B,現在問題是 類A 類B 類C都有一個 叫display()的函式
C c1;
c1.display()//此時該是誰呢,是最新的也就是c的display()。這個會覆蓋
此時要想訪問A的display(),則要限定作用域 。
比如 c.A::display();
16.虛基類
D 是 B 和 C 的派生類,B 和 C 又都是繼承了A,這樣會保留多份資料成員的拷貝
虛基類是的在繼承簡介共同基類時只保留一份
class A { A(int i){} ..... }; class B: virtual public A { B(int n):A(n){} ... }; class C: virtual public A { C(int n):C(n){} ... }; class D:public B,public C { D(int n):A(n),B(n),C(n){}//這個必須由最後的派生類中對直接基類和虛基類初始化 }
17.型別的轉化
派生類可以向基類物件賦值 (大材小用),也可以向積累物件的引用進行賦值或初始化
派生類物件的地址可以賦給基類物件的指標變數,也就是說,指向基類物件的指標變數也可以用來指向派生類物件
18.多型性
分為兩種 ,靜態多型性和動態多型性(啥玩意啊,玩的怪花(小聲bb))
靜態多型性 就是 函式過載 和運算子的過載
動態 就是通過虛擬函式來實現的
說一下虛擬函式,作用還是要解決繼承中的二義性問題,
解決方法是想通過指標的方法來實現
Student stu(...); Graduate grad(...);//假設grad是stu的派生,且兩者都有display函式 Student *p = &stu; p->display(); p = &grad;//想通過變換指標指向來,但單單的這樣做是不行的,因為這樣做會把grad型別強制轉化成student的型別 p->display();
解決上述問題的方法是將Student類中的display()函式前加上virtual
注意問題是 成原函式 定義為虛擬函式後,其派生類都為虛擬函式
使用方法是指向一個基類物件的指標變數,並使它指向同一類族中需要呼叫該函式的物件
19.虛解構函式
如下面程式碼
class Point{ public: point(); ~point(); }; class Circle: public Point { public: Circle(); ~Circle(); } int main() { Point *p = new Circle; delete p; return 0; }
new的一個物件,在釋放的時候,只會執行基類的解構函式,而不執行派生類的
解決方法是 在Point 的解構函式前加上 virtual
個人理解(這個virtual 在繼承中 都會遺傳)
20.純虛擬函式
先說一點吧,往往有一些類,他們不用來生成物件,唯一目的就是用它去建立派生類,叫做抽象類
比如,點 可以派生出 園 ,圓可以派生出圓柱體 ,但這些都是 shape 的直接派生或者間接派生
比如
class Shape{ public: virtual float area() const {return 0.0;}//虛擬函式 virtual float volume() const {return 0.0;}//虛擬函式 virtual void shapeName() const = 0; // 純虛擬函式 形式為 virtual 函式型別 函式名字 (引數列表) =0; };
最後來個差不多的
#include<iostream> using namespace std; class Shape { public: virtual float area() const {return 0.00;} virtual float volume() const {return 0.00;} virtual void ShapeName() const = 0; }; class Point: public Shape { public: Point(float a=0,float b=0): x(a), y(b){}; void SetPoint(float a,float b) { x=a,y=b; } float getX() const {return x;} float getY() const {return y;} virtual void ShapeName() const {cout<<"point"<<endl;} friend ostream &operator <<(ostream &,const Point &); protected: float x,y; }; ostream &operator <<(ostream &output,const Point &p) { output<<"["<<p.x<<","<<p.y<<"]"<<endl; return output; } class Circle: public Point { public: Circle(float x=0,float y=0,float r=0):Point(x,y),radius(r) {} void SetRaidus(float r){ radius = r;} float GetRadius() const {return radius;} virtual float area() const{ return 3.14 * radius * radius;} virtual void ShapeName() const {cout<<"Circle"<<endl;} friend ostream &operator <<(ostream &,const Circle &); protected: float radius; }; ostream &operator <<(ostream &out,const Circle &c) { out<<"["<<c.x<<" "<<c.y<<"]"<<endl; out<<"r="<<c.radius<<endl; return out; } class Yuan: public Circle { public: Yuan(float x=0,float y=0,float r=0,float h=0):Circle(x,y,r),height(h){} void SetHeight(float h) {height = h;} virtual float area() const {return 2 * Circle::area() + 2 * 3.14 * radius * height;} virtual float vulume() const {return Circle::area() * height;} virtual void ShapeName() const {cout<<"Yuan"<<endl;} friend ostream &operator <<(ostream &,const Yuan &); protected: float height; }; ostream & operator <<(ostream &out,const Yuan &Y) { out<<"["<<Y.x<<" "<<Y.y<<"],r="<<Y.radius<<"H="<<Y.height<<endl; return out; } int main() { Point point (3.2,4.5); Circle circle(2.4,1.2,5.6); Yuan yuan(3.4,6.4,5.2,10.5); point.ShapeName(); cout<<point<<endl; circle.ShapeName(); cout<<circle<<endl; yuan.ShapeName(); cout<<yuan<<endl; Shape *pt; pt=&point; pt->ShapeName(); pt=&circle; pt->ShapeName(); pt=&yuan; pt->ShapeName(); return 0; }
可能以和還會更新吧..............