拷貝建構函式與運算子的重載
* 多態
- 前堤:繼承,虛函數,指標或引用
- 類型轉換 : dynamic_cast 把父類*轉換成子類*
- 純虛函數 : 保證此函數一定不會被執行,抽象類別,不能建立對象.
* 友元
- 內部授權,不是成員,這一點很重要,因此也沒有this,與之相關的一切
也沒有.
- 打破封裝,必須有一個類的內部聲明,可訪問任何成員通過 對象.來訪
問.
* 靜態
- 是成員
- 大家共用一份成員,整個類共用的成員.
- 待用資料成員: 在外部初始化
- 靜態函數只能訪問靜態成員,類名::成員
====================================================
* 運算子多載(c++特有功能)
* 內部類
* 異常
拷貝建構函式除了名字特殊外毫無特殊之處.如下
C++代碼
A(const A& obj){
cout << "我是拷貝建構函式" << endl;
}
A a = obj// 與 A a(obj)是等價的
C++代碼
#include <iostream>
using namespace std;
class A {
int data;
public :
A():data(5){ //初始化列表初始化的是成員而不是形參
cout << "A()" << endl;
}
A(int d):data(d){
cout << "A(int)" << endl;
}
A(bool b):data(b?123:234){
cout << "A(bool)" << endl;
}
A(char c) : data((int)c){
cout << "A(char)" << endl;
}
A(const A& o) : data(o.data){
cout << "A(const A&)" << endl;
}
void show(){
cout << "data=" << data << endl;
}
virtual ~A(){
cout << "~A()" << endl;
}
};
int main()
{
A a1;//調用無參建構函式
A a2(28);
A a3(true);
A a4('S');
A a5(a2);
}
//用引用的好處
#include <iostream>
using namespace std;
class A {
int data;
public :
A():data(5){ //初始化列表初始化的是成員而不是形參
cout << "A()" << endl;
}
A(int d):data(d){
cout << "A(int)" << endl;
}
A(bool b):data(b?123:234){
cout << "A(bool)" << endl;
}
A(char c) : data((int)c){
cout << "A(char)" << endl;
}
//A(const A& o) : data(o.data){
// cout << "A(const A&)" << endl;
//}
//形參是一個新變數,在調用時建立,由實參來初始化.
A(A o) : data(o.data){ //這樣寫是錯誤的,進入死迴圈
cout << "A(const A&)" << endl;
}
//建立對象時要調建構函式,調建構函式建立形參,形參又是一個新對象,
//建立新對象又要調建構函式,調建構函式又要建立形參,.......
void show() const{
cout << "data=" << data << endl;
}
virtual ~A(){
cout << "~A()" << endl;
// show(200);
// show(false);
// show('A');
//A a2(28);
//A a3(true);
//A a4('S');
//A a5(a2);
}
};
void show( A obj)
{
obj.show();
}
void func( const A& obj)
{
obj.show();
}
int main()
{
A a1;//調用無參建構函式
show(a1);
func(a1);
}
當類無建構函式時,會自動產生A(),A(const A&)建構函式.
+ 當類成員有指標成員指向動態記憶體時預設拷貝建構函式會出問題.
因為它會使兩個指標指向同一個地方導致混亂.
這時我們需要重寫這個拷貝建構函式.如下樣本:
C++代碼
#include <iostream>
using namespace std;
class A {
int data;
public :
A():data(5){ //初始化列表初始化的是成員而不是形參
cout << "A()" << endl;
}
//拷貝建構函式
A(const A& o)/* :data(o.data) */ {
cout << "A(const A&)" << endl;
}
void show(){
cout << "data=" << data << endl;
}
virtual ~A(){
cout << "~A()" << endl;
}
};
int main()
{
A a1;
a1.show(); //輸出正常
A a2(a1);
a2.show(); //輸出垃圾資料,所以初始化不能少
}
在類的成員函數中可以訪問本類中任何成員變數.
自已定義拷貝建構函式之後,就沒有預設的拷貝建構函式了,要在
自己定義的拷貝建構函式中處理所有需要處理的資料.
C++代碼
//動態數組類,長度可動態變化.未完整版
#include <iostream>
using namespace std;
class Array {
char * p;
int len;
public :
Array(int n ) : len(n),p(NULL){
resize(n);
}
Array( const Array& o ) : len(o.len) {
p = new char[len];
for( int i=0; i<len; i++ )
p[i] = o.p[i];
}
void resize( int n ){
char * q = new char[n];
int min = (n<len?n:len);
if(p!=NULL){
for( int i=0; i<min; i++)
q[i] = p[i];
delete[] p;
}
p = q;
for( int i=min; i<n; i++)
p[i] = '/0';
len = n;
}
int size(){
return len;
}
void set( int index, char c ){
if( index<0 || index>len ){
cout << "Error" << endl;
return;
}
p[index] = c;
}
char get( int index ){
if( index<0 || index>len ){
cout << "Error" << endl;
return '^';
}
return p[index];
}
void fill ( char start, int skip ){
for( int i=0; i<len; i++)
p[i] = start + i*skip;
}
void show(){
for( int i=0; i<len; i++){
cout << p[i];
}
}
~Array(){
if(p!=NULL){
delete[ ] p;
p = NULL;
}
}
}; //end class Array
int main()
{
Array a1(10);
a1.fill('a',2);
a1.show();
cout << endl;
Array a2(a1);
a2.fill('A',2);
a2.show();
cout << endl;
a1.show();
cout << endl;
cout << "------------------" << endl;
cout << "Please input text(end by '$') : " << endl;
for(int i=0;;i++){
char c;
cin >> c;
if(c=='$')
break;
else if(i+1>a1.size()){
a1.resize(a1.size()+10);
}
a1.set(i,c);
}
a1.show();
cout << endl;
for( int i=0; i<a1.size(); i++){
cout << a1.get(i);
}
}
========================================
預設的拷貝建構函式是淺拷貝,自己寫的拷貝建構函式是深拷貝.
* 運算子函數
* 單目運算子
C++代碼
A operator+(A obj1,Aobj2){/* code */};
A a1(30),a2(50);
A result;
result = a1 + a2; //會去調A operator+(A obj1,Aobj2)
result.show();
C++代碼
//樣本
//由程式可知成員比友員要方便,少傳一個參數
#include <iostream>
using namespace std;
class A{
//code
A sub( const A& o){
return A(data-o.data);
}
A oprator-()/......
};
A operator-(const A& o1, const A& o2){
//code...
}
int main()
{
A obj1(40),obj2(50);
A obj3,obj4;
obj3 = obj1.sub(obj2);
obj3.show();
obj4 = obj1 - obj2; //會去調obj1.aperator-(..)
}
運算子多載就是自已寫運算子函數來規定運算子如何工作.
--------------------------
運算子函數的實現方法:
* 友元運算子函數:
- 在類中聲明friend
- 定義:傳回型別 operatorX(形參1,形參2)
- 調用: obj1 X obj2 解釋成 operatorX(obj1,obj2),傳回值作為運
算結果
* 成員函數: 直接在類中定義
- 定義: 傳回型別 operatorX(形參1)
- 調用: obj1 X obj2 解釋成 obj1.operator(obj2),傳回值作為運算
結果
cout << f1 << f2;//用重載運算子表示,只能通過友員來實現
// 如果要用成員函數,則會有cout.operator<<(const F& f),所以這是不
// 可能的.因此只能用友員來實現,operator<<(cout,f)
// 而cout是ostream型的,因此有以下標準格式.注意不能加const,因為//
cout是要改變的,會改變裡的緩衝成員.
ostream& operator<<( /* 不能加const */ ostream& cout, const
F&) //輸出運算子的標準重載格式.
friend istream& operator>>(istream& is, F& f){
}//輸入運算子多載標準格式
C++代碼
//重載運算子完整例子
//雙目運算子多載
#include <iostream>
using namespace std;
class F{
int n;
int d;
public :
F(int n=0, int d=1):n(n),d(d){}
friend ostream& operator<<(ostream& os, const F& f){
os << '[' << f.n << '/' << f.d <<']';
return os;
}
F operator*(const F& o) {
return F(n*o.n,d*o.d);
}
friend F operator/(const F& f1,const F& f2){
return F(f1.n*f2.d,f1.d*f2.n);
}
friend istream& operator>>(istream& is, F& f){
char ch;
is >> f.n >> ch >> f.d;
return is;
}
};
int main()
{
F f1(3,5),f2(11,7),f;
cout << f1 << '*' << f2 << '=' << f1*f2 << endl;
cout << f1 << '/' << f2 << '=' << f1/f2 << endl;
cout << "Input 2 fractions :";
cin >> f1 >> f2;
cout <<"f1=" << f1 << endl;
cout << "f2=" << f2 << endl;
}
* 單目運算子多載
-友元函數形式,傳回型別 operatorX(形參)
使用:X obj ---> operatorX(obj);
-成員函數形式 盡量用成員 傳回型別 operatorX(/*無形參*/)
使用: X obj ---> obj.operator();
c++代碼
//單目運算子多載
//把後++,後--當作雙目運算子,第二個運算元是整形.
#include <iostream>
using namespace std;
class A{
int data;
public :
A(int d=0):data(d){}
friend ostream& operator<<(ostream& os,const A& a){
os << a.data;
return os;
}
friend istream& operator>>(istream& is,A& a){
is >> a.data;
return is;
}
friend A& operator++(A& a){
a.data += 10;
return a;
}
A& operator--(){
data -= 10;
return *this;
}
//後加加,規定一個整形為形參,與先加加區分
friend A/* 此時不再用引用 */ operator++(A& a,int){
A old(a);
a.data += 1;
return old;
}
//後減減,規定為一個整形為形參,與先減減區分.
A /*不要引用*/ operator--(int){
A old(*this);
data -= 1;
return old;
}
};
int main()
{
A a1(50),a2(100);
cout << "a1=" <<a1 << endl;
cout << "a2=" <<a2 << endl;
cout << "++a1=" << ++a1 << endl;
cout << "--a1=" << --a1 << endl;
cout << "a1++=" << a1++ << endl;
cout << "a1=" <<a1 << endl;
cout << "a2--=" << a2-- << endl;
cout << "a2=" <<a2 << endl;
}
運算子多載提供了一個自己規定運算子式作方式的方法,至少有一個操
作數是自訂類型的.基本類型我們是規定不了的.
強制類型轉換:類型(資料) --> (不必寫傳回型別,因為始終與後面的類
型是相同的) operator類型(無形參) 只能寫成成員函數,不能是友員.
C++代碼
#include<iostream>
using namespace std;
class A{
int data;
public:
A(int d=0):data(d){}
operator int(){
return data;
}
operator bool(){
return data!=0;
}
operator char(){
return (char)data;
}
};
int main()
{
A a1(65),a2(200);
cout << "a1=" << (char)a1 << endl;
int d=a2;
if(a2)
cout << "good" << endl;
}
對自訂類型的對象,使用運算子時,總是調用相應的運算子函數
三目運算子不能重載.
等號是雙目運算子也可重載,它也只能是成員.
還有點號('.')不能重載.
雙冒號(::)不能重載.
sizeof(類型)沒法重載.
#號不是運算子,無所謂重載.
'= () [] -> 類型轉換'只能是成員函數來重載,其它的隨便,我們建議盡量
使用成員函數來寫,有點只能用友元,比如輸入輸出.
==================================================
+ 雙目運算子多載
- 友元形式:返 operator符號(形1,形2)
- 成員形式:返 operator符號(形)
+ 單目運算子多載
- 友元: 返 operator符號(形參)
- 成員: 返 operator符號()
+ 特例
- 加加
- 先加加
- 後加加 返回 operator符號(形,int)
- 減減
- 先減減
- 後減減
- 類型轉換 只能是成員perator 空格 類型();