




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
4.1類和對象(ClassesandObjects)
4.2構造函數與析構函數
(ConstructorsandDestructor)4.3常成員(ConstantMembers)4.4指向對象的指針(PointertoObject)
4.5靜態成員與友元(StaticMembersandFriend)4.6常見編程錯誤(CommonProgrammingErrors)本章小結(ChapterSummary)
習題4(Exercises4)
4.1.1類與抽象數據類型
(ClassandAbstractDataTypes)
抽象即忽略一個問題中與當前事物無關的方面,將注意力放在與當前事物有關的方面。它把一類事務的屬性抽象出來,同時封裝了該類事務可以提供的操作。本章我們來具體學習類這種抽象數據類型的定義以及相關的一些重要概念。4.1類和對象(ClassesandObjects)4.1.2類的聲明和定義
(ClassesDeclarationsandDefinitions)
1.從結構體到類
C++兼容C語言的結構體類型(struct),但又與C語言中的結構體類型有所不同。
例如:
structStud
{chargender;
intage;
}2.類的定義
類定義的語法格式為
classclass_name
{
//Member_list
};【例4-1】
類定義舉例。
classPerson
{
public:
voidwalk(){}
voidwork(){}
voiddisp();
private:
stringname;
unsignedage;
};4.1.3類的函數成員的實現
(ClassesMemberFunctions)
1.使用域限定符“::”
從例4-1的Person類中我們看到,函數成員disp沒有函數體,只有函數聲明部分。
在類外定義函數成員的語法格式為
return_typeclassname::functionname(formalparameter)
{
//functionbody
}套用此格式函數成員disp可以這樣來實現:
voidPerson::disp()
{
cout<<"此人姓名為:"<<name<<"年齡為:"<<age<<endl;
}2.內聯函數成員
類中的函數成員允許是內聯函數。
如
inlinevoiddisp();
或者:
inlinevoidPerson::disp()
{
cout<<"此人姓名為:"<<name<<"年齡為:"<<age<<endl;
}4.1.4類和對象(ClassesandObjects)
定義了某個類時,系統不分配內存空間。例如有如下3個Person類的對象:
Personp1,p2,p3;
類的對象及類的函數成員的關系如圖4-1所示。圖4-1類的對象及類的函數成員關系如果調用類的函數成員,如:
p1.disp();
p2.disp();
p3.disp();
此時p1、p2、p3這3個對象分別調用共享的函數成員disp。4.1.5類的訪問屬性(AccessControlsinClasses)
前面已經提到過封裝的概念,即把數據和函數包含在一個類類型中。
1.private和public
private即私有的,被用來隱藏類中的數據成員和函數成員;public即公有的,用來公開類中的數據成員和函數成員。例如:
classPerson
{public:
voidwalk(){}
voidwork(){}
voiddisp();
private:
stringname;
unsignedage;
};classPerson
{
private:
stringname;
unsignedage;
public:
voidwalk(){}
voidwork(){}
voiddisp();
};classPerson
{
public:
voidwalk(){}
voidwork(){}
private:
stringname;
unsignedage;
public:
voiddisp();
};
2.類域
類域(classscope)即類的作用域,類似于我們以前學過的函數域、塊域等。
例如:Personp1;
="zhangsan"; //Error!
cout<<p1.disp(); //OK!
3.get和set函數
類成員的訪問屬性實現了面向對象程序設計的封裝的概念以及信息隱藏的原理。
【例4-2】
帶有get、set函數的Person類。
classPerson
{
public:
voidset_name(stringnew_name);
voidset_age(unsignednew_age);
stringget_name(){returnname;}unsignedget_age(){returnage;}
voiddisp();
voidwalk(){}
voidoperate(){}
private:
stringname;
unsignedage;
};
4.struct和class的區別
C++的class類型是從C語言的struct發展而來的。
classC
{
intx;
public:
voidsetX(intx);
};
structC
{
voidsetX(intx);
private:
intx;
};4.2.1構造函數(Constructors)
構造函數是特殊的類函數成員,它用來初始化類的對象。構造函數可以在類中定義,或者在類中聲明在類外定義。構造函數可以根據需要定義多個,它們的函數名相同,互為重載函數,依據參數不同系統會自動區分該調用哪個構造函數。4.2構造函數與析構函數(ConstructorsandDestructor)【例4-3】
帶有構造函數的Person類。
classPerson
{
public:
Person(){}; //缺省構造函數
Person(stringnew_name,unsignednew_age);
//構造函數聲明
Person(Person&); //拷貝構造函數
voidset_name(stringnew_name);
voidset_age(unsignednew_age);stringget_name(){returnname;}
unsignedget_age(){returnage;}
voiddisp();
voidwalk(){}
voidoperate(){}
private:
stringname;
unsignedage;
};
Person::Person(stringnew_name,unsignednew_age) //構造函數定義{
name=new_name;
age=new_age;
}
Person::Person(Person&p)
{
name=;
age=p.age;
}intmain()
{
Personp1(“zhangsan”,20),p2(p1),p3;
p1.disp();
p2.disp();
p3.set_name(“lisi”);
p3.set_age(20);
p3.disp();
return0;
}4.2.2缺省構造函數(DefaultConstructors)
前面提到,如果類中沒有構造函數的定義,則系統會自動生成一個無參構造函數,我們稱之為缺省構造函數。以Person類為例:
classPerson
{
public:
voidset_name(stringnew_name);
voidset_age(unsignednew_age);
stringget_name(){returnname;}
unsignedget_age(){returnage;}
voiddisp();
voidwalk(){}
voidoperate(){}
private:
stringname;
unsignedage;
};4.2.3拷貝構造函數(CopyConstructors)
拷貝構造函數,顧名思義,即用已經存在的類的對象去構造一個新的對象,兩個對象的數據成員值是完全相同的。
【例4-4】
使用系統提供的拷貝構造函數。
#include<iostream>
usingnamespacestd;
classPoint
{
public:Point(intxx=0,intyy=0){X=xx;Y=yy;}
intGetX(){returnX;}
intGetY(){returnY;}
voidSetX(intx){X=x;}
voidSetY(inty){Y=y;}
private:
intX;
intY;};
intmain()
{PointA(1,2);
PointB(A);//拷貝構造函數被調用
cout<<A.GetX()<<","<<A.GetY()<<endl;
cout<<B.GetX()<<","<<B.GetY()<<endl;
return0;
}【例4-5】
拷貝構造函數被調用的幾種情況。
#include<iostream>
usingnamespacestd;
classPoint
{
public:Point(intxx=0,intyy=0){X=xx;Y=yy;}
Point(Point&p);
intGetX(){returnX;}
intGetY(){returnY;}
??voidSetX(intx){X=x;}
??voidSetY(inty){Y=y;}
private:
intX,Y;
};Point::Point(Point&p)
{
X=p.X;
Y=p.Y;
cout<<"Copyconstructoriscalled."<<endl;
}
PointFun(Pointp)
{
Pointp1;
p1.SetX(p.GetX()+2);
p1.SetY(p.GetY()+2);
returnp1;}
intmain()
{PointA(1,2);
PointB(A);//拷貝構造函數被調用
PointC;
C=Fun(A);
cout<<B.GetX()<<","<<B.GetY()<<endl;
cout<<C.GetX()<<","<<C.GetY()<<endl;
return0;
}【例4-6】
自定義拷貝構造函數。
#include<iostream>
#include<string>
usingnamespacestd;
classPersonlist
{
public:
Personlist(){};//缺省構造函數
Personlist(conststringnew_name[],intnew_size);
//構造函數聲明
voidset_name(conststring&new_name,inti);
voiddisp();
private:
string*namelist;//注意這里,namelist被定義為string指針
intsize;
};voidPersonlist::set_name(conststring&new_name,inti)
{namelist[i]=new_name;}
Personlist::Personlist(conststringnew_name[],intnew_size)
{
namelist=newstring[size=new_size];
for(inti=0;i<new_size;i++)namelist[i]=new_name[i];
}
voidPersonlist::disp()
{ cout<<"Thenamesare";
for(inti=0;i<size;i++)
cout<<namelist[i]<<"\t";
cout<<endl;
}
intmain()
{
stringnamelist[3]={"Tom","Jack","Allen"};Personlistp1(namelist,3);
Personlistp2(p1); //調用系統提供的拷貝構造函數
cout<<"改變之前"<<endl;
p1.disp();
p2.disp();
p2.set_name("Peter",1); //把p2中Jack的名字改為Peter
cout<<"改變之后"<<endl;
p1.disp();
p2.disp();
cout<<endl;
return0;
}程序運行的結果為:
改變之前
ThenamesareTom Jack Allen
ThenamesareTom Jack Allen
改變之后
ThenamesareTom Peter Allen
ThenamesareTom Peter Allen
所以p2改變后p1自然也隨之改變,反之若p1改變,p2也會隨之改變,如圖4-2所示。圖4-2使用系統提供拷貝構造函數自定義的拷貝構造函數:
Personlist::Personlist(Personlist&p)
{
namelist=0;
delete[]namelist;
if(list!=0)
{
namelist=newstring[size=p.size];
//給namelist重新分配空間for(inti=0;i<size;i++)
namelist[i]=list[i];
}
else
{namelist=0;size=0;}
}則上例的執行結果為:
改變之前
ThenamesareTom Jack Allen
ThenamesareTom Jack Allen
改變之后
ThenamesareTom Jack Allen
ThenamesareTom Peter Allen
使用自定義拷貝構造函數的示意圖如圖4-3所示。圖4-3使用自定義拷貝構造函數示意圖4.2.4轉換構造函數(ConvertConstructors)
構造函數還提供了一種自動類型轉換的功能,即把一種其他類型轉換為該類類型。
我們繼續以Person類為例:
classPerson
{
public:
Person(){}
Person(Person&p){name=;}Person(stringnew_name){name=new_name;}
voidset_name(stringnew_name){name=new_name;}
stringget_name(){returnname;}
voiddisp(){cout<<"Thisperson’snameis:"<<name<<endl;}
private:
stringname;
};我們在主函數中有如下對象的定義:
intmain()
{
stringname=“lisi”;
Personp1(“zhangsan”),p2;
p2=name;
p1.disp();
p2.disp();
return0;
}4.2.5析構函數(Destructor)
與萬事萬物都有產生和消亡一樣,程序中的對象也是一樣,也會消失。
1.析構函數的聲明及特點
簡單地說,構造函數是用來構造對象的,析構函數則是用來完成對象被刪除前的一些清理工作的。析構函數的聲明格式:
~類名();
如Person類的析構函數聲明應為
~Person();
析構函數名和構造函數一樣都和類名相同,不過析構函數在函數名前面多了一個“~”,析構函數不允許有參數,所以一個類中只能有唯一的析構函數。
2.構造函數和析構函數的調用順序
那么程序具體什么時候調用構造函數和析構函數呢?不同的對象先后順序定義會不會影響系統對構造函數和析構函數的調用呢?我們來看一個例子:
【例4-7】
構造函數和析構函數的調用順序。
#include<iostream>
usingnamespacestd;
classPerson
{
public:Person(char*input_name)
{name=newchar[20];
strcpy(name,input_name);
cout<<name<<"ConstructorCalled."<<endl;
}
~Person() //析構函數定義
{cout<<name<<"DestructorCalled."<<endl; deletename;}
voidshow();
private:
char*name;
};
voidPerson::show()
{cout<<name<<endl;}
intmain()
{
Personstudent1("zhangming");//構造函數調用
Personstudent2("lixin");//構造函數調用
student1.show();
student2.show();
return0;
}我們來看程序的運行結果:
zhangmingConstructorCalled.
lixinConstructorCalled.
zhangming
lixin
lixinDestructorCalled.
zhangmingDestructorCalled.4.3.1const修飾符(constModifier)
在第2章我們講到過使用const修飾簡單數據類型,從而使相應變量成為不能改變值的常變量,同樣類的對象也可以在定義時由const修飾,稱其為常對象。
常對象的語法格式為:
constclass_Typeobject_name;4.3常成員(ConstantMembers)或
class_Typeconstobject_name;
當聲明一個引用加上const符時,該引用稱為常引用。常引用所引用的對象不能被更新。一般引用作為形參時,可以對實參進行修改,但常引用則不允許發生對實參的意外修改。
常引用的語法格式為
constdata_type&reference_name;【例4-8】
常引用作形參。
程序如下:
#include<iostream>
usingnamespacestd;
voiddisplay(constint&d);
intmain()
{intd(2008);display(d);
return0;
}
voiddisplay(constint&d)
{
d=d+5;//錯誤!常引用不能被更改
cout<<d<<endl;
}4.3.2常數據成員(ConstantDataMembers)
和一般類型數據一樣,類的數據成員也可以是常量和常引用。
【例4-9】
常數據成員舉例。
classPoint
{
public:
Point(doublenew_x,doublenew_y);
Point(constPoint&p);
voiddisp();private:
doublex;
constdoubley; //常數據成員y
};
Point::Point(doublenew_x,doublenew_y):y(new_y)
//初始化列表初始化常數據成員y
{
x=new_x;
}
Point::Point(constPoint&p):y(p.y)//初始化列表初始化常數據成員y
{
x=p.x;
}
voidPoint::disp()
{
cout<<"該點的坐標為:("<<x<<","<<y<<")"<<endl;
}intmain()
{
Pointp1(1,2),p2(p1);
p1.disp();
p2.disp();
return0;
}
程序運行結果為:
該點的坐標為:(1,2)
該點的坐標為:(1,2)例如:
classPoint
{
public:
Point(doublenew_x,doublenew_y);
Point(constPoint&p);
voiddisp();private:
doublex;
staticconstdoubley; //靜態常數據成員
};
constdoublePoint::y=2.0;4.3.3常函數成員(ConstantFunctionMembers)
const不僅可以用來修飾類的數據成員,也可以用來修飾類的函數成員。被const修飾的函數成員我們稱之為常函數成員,其說明格式為:
return_typefunction_name(formalparameterslist)const;
const關鍵字可以被用于參與對重載函數的區分,例如,如果在類中有這樣的函數成員的聲明:
voiddisp();
voiddisp()const;【例4-10】
常函數成員舉例。
#include<iostream>
usingnamespacestd;
classPoint
{
public:
Point(doublenew_x,doublenew_y);
Point(constPoint&p);
voiddisp();
voiddisp()const;private:
doublex;
constdoubley;
};
Point::Point(doublenew_x,doublenew_y):y(new_y)
{
x=new_x;
}
Point::Point(constPoint&p):y(p.y)
{ x=p.x;
//y=p.y;
}
voidPoint::disp()const
{
cout<<"您正在調用一個常函數成員,";
cout<<"該點的坐標為:("<<x<<","<<y<<")"<<endl;
}
voidPoint::disp()
{
cout<<"該點的坐標為:("<<x<<","<<y<<")"<<endl;}
intmain()
{
Pointp1(1,2),p2(p1);
constPointp(4,5); //p為常對象
p1.disp();
p2.disp();
p.disp(); //p調用常函數成員disp
return0;
}程序運行結果為:
該點的坐標為:(1,2)
該點的坐標為:(1,2)
您正在調用一個常函數成員,該點的坐標為:(4,5)4.4.1對象指針(ObjectPointer)
和一般數據類型的變量一樣,每一個類的對象在初始化之后都會在內存中占有一定的空間。4.4指向對象的指針(PointertoObject)對象指針遵循一般變量指針的各種規則,聲明對象指針的一般語法形式為
classname*對象指針名;
例如:
Person*p_Person; //聲明Person類的對象指針變量p_Person
Personp1; //聲明Person類的對象p1
p_Person=&p1; //將對象p1的地址賦給p_Person,使p_Person指向p1【例4-11】
使用指針訪問Person類的成員。
#include<iostream>
usingnamespacestd;
intmain()
{
Personp,*p_Person=&p;
p_Person->set_name("LiMing");
p_Person->set_age(20);
p_Person->disp();
return0;
}4.4.2
this指針(thisPointer)
this指針是一個隱含于每一個類的函數成員中的特殊指針(包括構造函數和析構函數),它指向正在被函數成員操作的對象。
我們來回顧一下前面Person類的構造函數:
Person::Person(stringnew_name,unsignednew_age)
//構造函數定義
{
name=new_name;
age=new_age;
}4.5.1靜態數據成員與靜態函數成員
(StaticDataMembersandFunctionMembers)
一般地,類的對象包括了類的所有的數據成員,但是,這里我們要學的靜態數據成員不屬于任何類的對象,它屬于某個類。和靜態數據成員類似,類也可以有自己的靜態函數成員,它沒有this指針,并且只能訪問該類的靜態數據成員。4.5靜態成員與友元(StaticMembersandFriend)
1.靜態數據成員的定義及初始化
定義一個靜態數據成員的語法格式為
staticdatatypevariablename;
【例4-12】
帶有靜態數據成員的Person類。
classPerson
{
public:
voidset_name(stringnew_name);
voidset_age(unsignednew_age);
stringget_name(){returnname;}
unsignedget_age(){returnage;}voiddisp();
voidwalk(){}
voidoperate(){}
private:
stringname;
unsignedage;
staticintcount;//定義靜態數據成員count
};
intPerson::count=0;
//使用域限定符在類外初始化靜態數據成員
2.靜態數據成員的訪問
訪問靜態數據成員的方法有以下兩種:
(1)通過類名及域限定符直接訪問;
(2)通過對象名訪問。
為了便于說明問題,我們把上例Person類中的靜態數據成員count訪問屬性改為public。我們在主函數中有如下的調用:intmain()
{
Personp;
p.count=3;
cout<<p.cout<<endl;
Person::count=5;
cout<<Person::count<<endl;
return0;
}運行結果為:
3
5
即通過這兩種訪問方式都可以改變靜態數據成員count的值。和其他類的數據成員一樣,我們一般把靜態數據成員的訪問屬性定義為private,如果要對其進行訪問等操作,我們可以通過相應的函數成員來實現。
3.靜態函數成員
靜態函數成員和其他函數成員一樣,屬于類而非類的對象,但它和其他函數成員的不同之處在于,靜態函數成員只能改變類的靜態成員(包括靜態數據成員和靜態函數成員),其他函數成員則可以訪問類的所有成員。
靜態函數成員的聲明格式如下:
staticreturn_typeFunction_name(formalparameterslist);【例4-13】
靜態成員的作用。
#include<iostream>
#include<string>
usingnamespacestd;
classPerson
{
public:Person(stringnew_name,unsignednew_age);
Person(constPerson&p);
Person(){count++;}
voidset_name(stringnew_name);
voidset_age(unsignednew_age);
stringget_name(){returnname;}
unsignedget_age(){returnage;}
voiddisp();
staticintgetCount();private:
stringname;
unsignedage;
staticintcount; //定義靜態數據成員count
};
intPerson::count=0;
//使用域限定符在類外初始化靜態數據成員
Person::Person(stringnew_name,unsignednew_age)
{ name=new_name;
age=new_age;
count++;
}
Person::Person(constPerson&p)
{
name=;
age=p.age;
count++;}
intPerson::getCount() //靜態函數成員在類外定義
{
returncount;
}
intmain()
{
Personp1,p2,p3;
Personp4("xiaoming",21),p5(p4);
cout<<"當前Person類對象的個數為:";
cout<<Person::getCount()<<endl;
return0;
}
程序運行結果為:
當前Person類對象的個數為:54.5.2友元函數與友元類
(FriendFunctionsandFriendClasses)
友元提供了不同類或對象的函數成員之間、類的函數成員與一般函數之間進行數據共享的機制。
1.友元函數
假設我們有一個復數類Complex:
classComplex
{
public: Complex();
Complex(constComplex&);
Complex(doublere,doubleim);
voidset_real(doublere){real=re;}
voidset_imag(doubleim){imag=im;}
doubleget_real(){returnreal;}
doubleget_imag(){returnimag;} voiddisp(){cout<<real<<"+"<<imag<<"i";}
private:
doublereal;
doubleimag;
};
Complex::Complex(doublere,doubleim)
{
real=re;imag=im;}
Complex::Complex(constComplex&comp)
{
real=comp.real;
imag=comp.imag;
}如果要實現兩個復數類對象的比較,即看兩個復數是否相等,我們可以定義如下非函數成員:
boolequal(Complexc1,Complexc2)
{
if((c1.get_real()==c2.get_real())&&
(c1.get_imag()==c2.get_imag()))
returntrue;
elsereturnfalse;
}【例4-14】
友元函數的使用。
#include<iostream>
usingnamespacestd;
classComplex
{
public:
Complex();
Complex(constComplex&);
Complex(doublere,doubleim); friendboolequal(Complexc1,Complexc2);
//友元函數聲明
voidset_real(doublere){real=re;}
voidset_imag(doubleim){imag=im;}
doubleget_real(){returnreal;}
doubleget_imag(){returnimag;}
voiddisp(){cout<<real<<"+"<<imag<<"i";}
private: doublereal;
doubleimag;
};
Complex::Complex(doublere,doubleim)
{
real=re;imag=im;
}
Complex::Complex(constComplex&comp)
{ real=comp.real;
imag=comp.imag;
}
boolequal(Complexc1,Complexc2) //友元函數實現
{
if((c1.real==c2.real)&&(c1.imag==c2.imag))
returntrue;
else
returnfalse;}
intmain()
{
Complexc1(2,3),c2(3,4);
if(equal(c1,c2))
cout<<"這兩個復數相等!"<<endl;
else
cout<<"這兩個復數不相等!"<<endl;
return0;
}程序運行結果為:
這兩個復數不相等!
這樣一個判斷兩個復數是否相等的函數,由于直接訪問了復數類的private數據成員,避免了頻繁調用函數成員,效率就高多了。
友元函數和類的函數成員一樣,可以訪問類中所有的成員。不只是非函數成員,一個類的函數成員也可以是另一個類的友元。【例4-15】
函數成員作為友元。
classStudent;
classTeacher
{
public:
//...
voidgradeofcourse(Student&s);
private:
intnumberofstu;
//...};
classStudent
{
public:
//...
friendvoidTeacher::gradeofcourse(Student&s);
private: floatgrad;
Teacher*pT;
//...
};
voidTeacher::gradeofcourse(Student&s)
{
s.grad=5; //直接訪問到其private數據成員grad
}
2.友元類
前面講到,一個類的函數成員也可以是另一個類的友元。當一個類中的所有函數成員都是另一個類的友元的時候,我們可以定義整個類是另一個類的友元,此時該友元稱為友元類。
例如,下面的代碼是把整個Teacher類作為Student類的友元類,即Teacher類的所有的函數成員都可以訪問Student類的所有成員。classStudent;
classTeacher
{
public:
//...
voidgradeofcourse(Student&s);
private:
intnumberofstu;
//...
};classStudent
{
public:
//...
friendclassTeacher;
private:
floatgrad;
//...
};
1.類的聲明中忘記結尾的分號“;”是錯誤的,如:
classPoint
{//…
}//錯誤:沒有分號
正確的語法是:
classPoint
{//…
};4.6常見編程錯誤(CommonProgrammingErrors)
2.先有類類型的聲明,再有類的對象的定義。
Pointp1,p2;//錯誤:類Point還沒有定義,不能定義其對象
classPoint
{//…
};?//類Point必須在對象p1和p2定義之前聲明3.類的函數成員和普通函數是不同的。
classPoint
{public:
voidDraw(){/*…*/}
//…
};intmain()
{Pointp;
Draw();//錯誤:Draw是類的函數成員,不是普通函數,調用時需要指定具體的對象
p.Draw();//正確
//…
}
4.如果一個類沒有構造函數,那么在定義該類的對象時對象的成員是沒有被初始化的,如:
classPoint
{public:
intgetX()const{returnx;}
intgetY()const{returny;}
private:
intx;inty;
};
intmain()
{Pointp;
cout<<p.getX()<<p.getY()<<endl;
//警告:隨機值將被輸出
//...
}添加缺省的構造函數后為:
classPoint
{public:
Point(){x=10;y=10;} //x,y被初始化
intgetX()const{returnx;}
intgetY()const{returny;}
private:
intx;
inty;};
intmain()
{Pointp;
cout<<p.getX()<<p.getY()<<endl; //正確
//...
}5.類的構造函數的參數不能是一個單一的類類型。
classPoint
{public:
Point(Pointpobj); //錯誤:單一的參數不能是Point類型
Point(Pointpobj,intn);
//正確,除了Point類型參數外,還有一個int類型參數
};拷貝構造函數確實只有一個Point類型參數,但要注意此時必須是引用:
classPoint
{public:
Point(Point&pobj);//正確
};
6.通過賦值語句改變常數據成員的值是錯誤的,即使在構造函數中也是錯誤的:
classPoint
{public:
Point(){c=0;}//錯誤:c是一個常數據成員
private:
constintc;//常數據成員c
};常數據成員必須在構造函數的初始化列表部分初始化,如下所列:
classPoint
{public:
Point():c(0){} //正確
private:
constintc; //常數據成員c
};
7.類的靜態函數成員是類的方法,類的非靜態函數成員是對象的方法,二者的調用方式有一定區別。
classPoint
{public:
voidDraw(){/*…*/} //非靜態:對象方法
staticvoids(){/*…*/} //靜態:類方法
//…
};intmain()
{Pointp;
p.Draw(); //正確
p.s();
//正確
Point::s(); //正確,s是靜態函數成員
Point::Draw(); //錯誤,Draw是非靜態函數成員
//…
}
8.如果靜態數據成員在類的聲明中聲明,則該數據成員需要在所有語句塊之外定義。
classPoint
{staticintx;//聲明靜態數據成員x
//…
};
intmain()
{intPoint::x;//錯誤:x定義在一個語句塊中
//…
}正確的定義是:
classPoint
{public:
staticintx;//聲明靜態數據成員x
//…
};
intPoint::x;//定義靜態數據成員
intmain()
{//…
}
9.類的對象或引用訪問成員時不能使用間接訪問符“->”。
classPoint
{public:
voidDraw(){/*…*/}
};
intmain()
{Pointp;p->m();//錯誤:p是一個對象,不是指針,正確的語法是p.m();
//…
}
voidf(Point&pr)
{pr->m();
//錯誤:pr是一個引用,不是指針,應該是pr.m();
}
10.指針this是一個常量,因此this不能進行賦值、自增或自減運算。
11.在靜態函數成員中不能使用this指針。
12.通過常函數成員改變一個數據成員的值是錯誤的,像賦值表達式等。
13.常函數成員不能調用非常函數成員。
14.如果函數f有一個常對象參數obj,那么對f來說,調用任何obj的非常函數成員都是錯誤的,因為f可以間接改變obj的數據成員,從而改變obj的狀態。面向對象程序設計通過抽象、封裝、繼承和多態使程序代碼實現可重用和可擴展,從而提高軟件的生產效率,減少軟件開發和維護的成本。本章重點介紹了類的定義、類的成員的訪問屬性以及類的兩種特殊的函數成員——構造函數和析構函數。友元出現的原因在于為了提高程序的運行效率,但友元也在一定程度上破壞了類的信息隱藏原則,一般建議在運算符重載時使用友元。本章小結(ChapterSummary)一、選擇題(至少選一個,可以多選)
1.以下不屬于類訪問權限的是()。
A.?public B.?static C.?protectedD.?private
2.有關類的說法不正確的是()。
A.類是一種用戶自定義的數據類型
B.只有類的函數成員才能訪問類的私有數據成員
C.在類中,如不做權限說明,所有的數據成員都是公有的
D.在類中,如不做權限說明,所有的數據成員都是私有的習題4(Exercises4)3.在類定義的外部,可以被任意函數訪問的成員有()。
A.所有類成員 B.?private或protected的類成員
C.?public的類成員 D.?public或private的類成員
4.關于類和對象的說法()是錯誤的。
A.對象是類的一個實例
B.任何一個對象只能屬于一個具體的類
C.一個類只能有一個對象
D.類與對象的關系和數據類型與變量的關系相似
5.設MClass是一個類,dd是它的一個對象,pp是指向dd的指針,cc是dd的引用,則對成員的訪問,對象dd可以通過()進行,指針pp可以通過()進行,引用cc可以通過()進行。
A.∷ B.?. C.?& D.
->
6.關于函數成員的說法中不正確的是()。
A.函數成員可以無返回值 B.函數成員可以重載
C.函數成員一定是內聯函數
D.函數成員可以設定參數的默認值7.下面對構造函數的不正確描述是()。
A.系統可以提供默認的構造函數
B.構造函數可以有參數,所以也可以有返回值
C.構造函數可以重載
D.構造函數可以設置默認參數
8.假定A是一個類,那么執行語句“Aa,b(3),*p;”調用了()次構造函數。
A.?1 B.?2 C.?3 D.?49.下面對析構函數的描述正確的是()。
A.系統可以提供默認的析構函數
B.析構函數必須由用戶定義
C.析構函數沒有參數
D.析構函數可以設置默認參數
10.類的析構函數是()時被調用的。
A.類創建B.創建對象C.引用對象D.釋放對象
11.創建一個類的對象時,系統自動調用();撤銷對象時,系統自動調用()。
A.函數成員B.構造函數C.析構函數D.復制構造函數12.通常拷貝構造函數的參數是()。
A.某個對象名 B.某個對象的成員名
C.某個對象的引用名D.某個對象的指針名
13.關于this指針的說法正確的是()。
A.?this指針必須顯式說明
B.當創建一個對象后,this指針就指向該對象
C.函數成員擁有this指針
D.靜態函數成員擁有this指針14.下列關于子對象的描述中,()是錯誤的。
A.子對象是類的一種數據成員,它是另一個類的對象
B.子對象可以是自身類的對象
C.對子對象的初始化要包含在該類的構造函數中
D.一個類中能含有多個子對象作其成員
15.對new運算符的下列描述中,()是錯誤的。
A.它可以動態創建對象和對象數組
B.用它創建對象數組時必須指定初始值
C.用它創建對象時要調用構造函數
D.用它創建的對象數組可以使用運算符delete來一次釋放16.對delete運算符的下列描述中,()是錯誤的。
A.用它可以釋放用new運算符創建的對象和對象數組
B.用它釋放一個對象時,它作用于一個new所返回的指針
C.用它釋放一個對象數組時,它作用的指針名前須加下標運算符[]
D.用它可一次釋放用new運算符創建的多個對象
17.關于靜態數據成員,下面敘述不正確的是()。
A.使用靜態數據成員,實際上是為了消除全局變量
B.可以使用“對象名.靜態成員”或者“類名∷靜態成員”來訪問靜態數據成員
C.靜態數據成員只能在靜態函數成員中引用
D.所有對象的靜態數據成員占用同一內存單元18.對靜態數據成員的不正確描述是()。
A.靜態成員不屬于對象,是類的共享成員
B.靜態數據成員要在類外定義和初始化
C.調用靜態函數成員時要通過類或對象激活,所以靜態函數成員擁有this指針
D.只有靜態函數成員可以操作靜態數據成員
19.下面的選項中,靜態函數成員不能直接訪問的是()。
A.靜態數據成員 B.靜態函數成員
C.類以外的函數和數據 D.非靜態數據成員20.在類的定義中,引入友元的原因是()。
A.提高效率 B.深化使用類的封裝性
C.提高程序的可讀性 D.提高數據的隱蔽性
21.友元類的聲明方法是()。
A.?friendclass<類名>; B.?youyuanclass<類名>;
C.?classfriend<類名>; D.?friendsclass<類名>;22.下面對友元的錯誤描述是()。
A.關鍵字friend用于聲明友元
B.一個類中的函數成員可以是另一個類的友元
C.友元函數訪問對象的成員不受訪問特性影響
D.友元函數通過this指針訪問對象成員
23.下面選項中,()不是類的函數成員。
A.構造函數 B.析構函數
C.友元函數 D.拷貝構造函數二、填空題
1.類定義中關鍵字
、
和
以后的成員的訪問權限分別是私有、公有和保護。如果沒有使用關鍵字,則所有成員默認定義為private權限。具有public訪問權限的數據成員才能被不屬于該類的函數所直接訪問。
2.定義函數成員時,運算符“∷”是
,“MyClass∷”用于表明其后的函數成員是在“MyClass類”中說明的。
3.在程序運行時,通過為對象分配來創建對象。在創建對象時,使用類作為樣板,故稱
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 軟件測試質量評估的常用方法試題及答案
- 委托簽合同協議書怎么寫
- 購買酒水合同協議書范本
- 邏輯思維能力測試題集試題及答案
- 車子出租合同協議書范本
- Access與Excel聯動的試題及答案
- 2025年嵌入式開發工具試題及答案解讀
- 2025年嵌入式技術發展動態試題及答案
- 沒簽勞動合同 協議書
- 計算機二級C語言模擬考試卷試題及答案
- 4P營銷理論課件
- 《夏季養生保健常識》課件
- 2025版亞馬遜FBA物流倉儲及電商運營服務合同6篇
- 幕墻工程施工方案及述標文件
- 《生鮮農產品供應鏈中雙渠道模式合作演化博弈實證研究》17000字
- 湖北省武漢市華師一附中2025屆中考生物押題試卷含解析
- 竣工結算審計服務投標方案(2024修訂版)(技術方案)
- 某藥業公司管理制度匯編
- 《佛與保險》課件
- 第7課《全球航路的開辟和歐洲早期殖民擴張》中職高一下學期高教版(2023)世界歷史全一冊
- 端午養生與中醫智慧
評論
0/150
提交評論