二級C++輔導筆記:類的其他幾點問題
一、拷貝構造函數(shù)
拷貝構造函數(shù)在下列情況下被調用:用已經存在的對象去初始化同一個類的另一個對象;在函數(shù)的參數(shù)中,以傳值方式傳遞類對象的拷貝;類對象的值被用做函數(shù)的返回值?截悩嬙旌瘮(shù)和前面說到的轉換構造函數(shù)有些相似。轉換構造函數(shù)是把一個類的對象轉化為另一個類的對象;拷貝構造函數(shù)是用一個已經存在的對象的值實例化該類的一個新對象。
不同對象間的初始化和賦值的區(qū)別:賦值操作是在兩個已經存在的對象間進行的;而初始化是要創(chuàng)建一個新的對象,并且其初值來源于另一個已存在的對象。編譯器會區(qū)別這兩種情況,賦值的時候調用重載的賦值運算符,初始化的時候調用拷貝構造函數(shù)。
如果類中沒有拷貝構造函數(shù),則編譯器會提供一個默認的。這個默認的拷貝構造函數(shù)只是簡單地復制類中的每個成員。
#include iostream.h
#include string.h
class Date
{
int mo, da, yr;
char* month;
public:
Date(int m = 0, int d = 0, int y = 0);
Date(const Date &);
~Date();
void display() const;
};
Date::Date(int m, int d, int y)
{
static char* mos[] =
{
January, February, March, April, May, June,
July, August, September, October, November, December
};
mo = m; da = d; yr = y;
if (m != 0)
{
month = new char[strlen(mos[m-1])+1];
strcpy(month, mos[m-1]);
}
else
month = 0;
}
Date::Date(const Date & dt)
{
mo = dt.mo;
da = dt.da;
yr = dt.yr;
if (dt.month != 0)
{
month = new char [strlen(dt.month)+1];
strcpy(month, dt.month);
}
else
month = 0;
}
Date::~Date()
{
delete [] month;
}
void Date::display() const
{
if (month != 0)
cout << month <<' '<< da << , << yr << std::endl;
}
int main()
{
Date birthday(6,24,1940);
birthday.display();
Date newday = birthday;
newday.display();
Date lastday(birthday);
lastday.display();
return 0;
}
本例中,用到了兩次拷貝構造函數(shù)。一個是使用普通的C++初始化變量的語句:
Date newday = birthday;
另一個是使用構造函數(shù)的調用約定,即把初始化值作為函數(shù)的參數(shù):
Date lastday(birthday);
二、類的引用
在函數(shù)參數(shù)和返回值中,如果一定要使用傳值方式,那么使用類對象的引用,是一個提高效率的方法。
類的數(shù)據(jù)成員也可以是一個引用,但必須注意:第一,一個引用必須初始化。通常一個類對象并不會像結構那樣用大括號來初始化,而是調用構造函數(shù)。因此在構造函數(shù)里必須初始化類當中的引用成員。第二,引用是一個別名。盡管類里面的引用在使用方式上看起來和類的一般數(shù)據(jù)成員沒有什么區(qū)別,但是作用在其上的操作,實際上是對用來初始化它的那么對象進行的。
#include iostream.h
class Date
{
int da, mo, yr;
public:
Date(int d,int m,int y)
{ da = d; mo = m; yr = y; }
void Display() const
{ cout << da << '/' << mo << '/' << yr; }
};
class Time
{
int hr, min, sec;
public:
Time(int h, int m, int s)
{ hr = h; min = m; sec = s; }
void Display() const
{ cout << hr << ':' << min << ':' << sec; }
};
class DateTime
{
const Date & dt;
const Time & tm;
public:
DateTime(const Date & d, const Time& t) : dt(d), tm(t)
{
//empty
}
void Display() const
{
dt.Display();
cout << ' ';
tm.Display();
}
};
int main()
{
Date today(7,4,2004);
Time now(15,20,0);
DateTime dtm(today, now);
dtm.Display();
return 0;
}
我們來看看這個程序中DateTime的構造函數(shù)的格式:冒號操作符引出了一個參數(shù)初始化表。必須使用這種格式來初始化引用數(shù)據(jù)成員,而不可以在函數(shù)體內來進行初始化工作。如果構造函數(shù)像上例一樣不是內聯(lián)的,那么最好不要在類聲明中構造函數(shù)的原型上使用冒號和初始化值表,而是像下面這樣,把參數(shù)初始化表放在定義構造函數(shù)的地方:
Class DateTime
{
const Date & dt;
const Time & tm;
public:
DateTime(const Date & d,const Time& t);
}
DateTime::DateTime(const Date & d,const Time& t):dt(d),tm(t)
{
//empty
}
可以使用構造函數(shù)的參數(shù)初始化表來初始化任何數(shù)據(jù)成員。特別是常量數(shù)據(jù)成員,和引用一樣,只能在參數(shù)初始化表里進行初始化,這是因為不可以在構造函數(shù)內部為常量數(shù)據(jù)成員賦值。
當一個類含有引用數(shù)據(jù)成員時,一旦引用被實例化和初始化以后,就無法修改它的值,所以該類不可能徹底地重載賦值運算符函數(shù)。
三、構造函數(shù)的參數(shù)初始化表
如果類對象的某些數(shù)據(jù)成員沒有載構造函數(shù)內部被初始化,那么必須使用構造函數(shù)的參數(shù)初始化表對他們進行初始化。否則,編譯器不止到該如何初始化這些還等著在構造函數(shù)內部賦值的成員。我們習慣用參數(shù)初始化表來初始化所有數(shù)據(jù)成員。
class Date
{
int mo,da,yr;
public:
Date(int m=0,int d=0,int y=0);
};
class Employee
{
int empno;
Date datehired;
public:
Employee(int en,Date & dh);
};
可以用下面兩種方法編寫Employee類的構造函數(shù):
Employee::Employee(int en,Date & dt)
{
empno=en;
datehired=dh;
}
或者;
Employee::Employee(int en,Date & dt):empno(en),datehired(dh)
{
//empty
}
雖然這兩種方法效果是一樣的,但是根據(jù)Date對象默認構造函數(shù)的復雜性的不同,這兩種形式的效率差別是很大的。
四、對const修飾符的簡單說明
如果一個對象被聲明為常量,那么該對象就不可以調用類當中任何非常量型的成員函數(shù)(除了被編譯器隱式調用的構造函數(shù)和析構函數(shù))?聪旅娴拇a;
#include iostream.h
class Date
{
int month,day,year;
public:
Date(int m,d,y):month(m),day(d),year(y) {}
void display()
{
cout < }
}
int main()
{
const Date dt(4,7,2004);
dt.display(); //error
return 0;
}
這個程序盡管編譯時沒有問題,但運行時卻出錯了。這是因為常量對象不能調用非常量函數(shù)。編譯器只看函數(shù)的聲明,而不在乎函數(shù)的具體實現(xiàn)。實際上函數(shù)的實現(xiàn)可以在程序中的任何地方,也可以是在另一個源代碼文件中,這就超過了編譯器的當前可見范圍。
//date.h
class Date
{
int month,day,year;
public:
Date(int m,d,y);
void display();
};
//date.cpp
#include iostream.h
#include date.h
Date::Date(int m,d,y):month(m),day(d),year(y) {}
void Date::display()
{
cout < }
//program.cpp
#include iostream.h
#include date.cpp
int main()
{
const Date dt(4,7,2004);
dt.display();
return 0;
}
解決出錯的問題有兩個方法:第一是聲明display()函數(shù)為常量型的
//in date.h
void display() const
//int date.cpp
void Date::display() const
{
cout < }
另一個解決方式就是省略掉Date對象聲明里的const修飾符。
Date dt(4,7,2004);
還有另一個容易出錯的地方:
void abc(const Date & dt)
{
dt.display(); //error 提示display沒有const修飾符
}
函數(shù)abc()聲明了一個Date對象的常量引用,這說明該函數(shù)不會修改傳遞進來的參數(shù)的值。如果Date::display()函數(shù)不是常量型的,那么在函數(shù)abc()里就不能調用它,因為編譯器會認為Date::display()函數(shù)有可能會修改常量的值。
不論類對象是否是常量型的,它必須修改某個數(shù)據(jù)成員的值時,ANSI委員會設立了mutable關鍵字。
五、可變的數(shù)據(jù)成員
假設需要統(tǒng)計某個對象出現(xiàn)的次數(shù),不管它是否是常量。那么類當中就應該有一個用來計數(shù)的整型數(shù)據(jù)成員。只要用mutable修飾符來聲明該數(shù)據(jù)成員,一個常量型的成員函數(shù)就可以修改它的值。
#include iostream.h
class AValue
{
int val;
mutable int rptct;
public:
AValue(int v) : val(v), rptct(0) { }
~AValue()
{
cout < }
void report() const;
};
void AValue::report() const
{
rptct++;
cout << val << endl;
}
int main()
{
const AValue aval(123);
aval.report();
aval.report();
aval.report();
return 0;
}
相關推薦:
北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內蒙古 |