C++面向对象程序设计(二)
二、类和对象基础
1.类和对象的基本概念
在类的定义中,用下列访问范围关键字来说明类成员可被访问范围:
- private:私有成员,只能在成员函数内访问
- public:共有成员,可以在任何地方访问
- protected:保护成员
以上三种关键字出现的次数和先后次序都没有限制。
class claaName{
private:私有属性和函数
public:共有属性和函数
protected:保护属性和函数
};
如果某个成员前面没有上述关键字,则缺省地被认为是私有成员。
class Man{
int nAge;
char szName[20];
public:
void SetName(char * szName){
strcpy(Man::szName,szName);
}
};
class CEmployee{
private:
char szName[30];
public:
int salary;
void setName(char * name);
void getName(char * name);
void averageSalary(CEmployee e1,CEmployee e2);
};
void CElemployee::setName(char * name){
strcpy(szName,name);
}
void CEmployee::getName(char * name){
strcpy(name,szName);
}
void CEmployee::averageSalary(CEmployee e1,Cemployee e2){
cout<<e1.szName;//ok,访问同类其它对象私有成员
salary=(e1.salary+e2.salary)/2;
}
int main(){
CEmployee e;
strcpy(e.szName,"Tom123456789");//编译出错,不能访问私有成员
strcpy("Tom");//ok
e.salary=5000;//ok
return 0;
}
“隐藏”的作用
如果将上面的程序移植到内存空间紧张的手持设备上,希望将szName改为 char szName[5],若szName不是私有,那么就要找出所有类似strcpy(e.szName,“Tom123456789”);这样的语句进行修改,以防数组越界,这样做会很麻烦。
如果将szName变为私有,那么程序中就不可能出现(除非在类的内部)strcpy(e.szName,“Tom123456789”); 这样的语句,所有对szName 的访问都是通过成员函数来进行,比如:e.setName(“Tom123456789”);
那么就算szName改短了,上面的语句也不需要找出来修改,只要改setName成员函数,在里面确保不越界就可以了。
struct CEmployee {
char szName[30];//公有
public:
int salary;
void setName(char * name);
void getName(char * name);
void averageSalary(CEmployee e1,CEmployee e2);
};
和用class的唯一区别,就是未说明公有还是私有的成员,就是公有。
成员函数也可以重载(注意避免二义性的产生)
成员函数可以带缺省参数(给定默认值)
#include <iostream>
using namespace std;
class Location{
private:
int x,y;
public:
void init(int x=0,int y=0);
void valueX(int val){x=val;}
int valueX(){return x;}
};
void Location::init(int X,int Y){
x=X;
y=Y;
}
int main(){
Location A,B;
A.init(5);
A.valueX(5);
cout<<A.valueX();//输出 5
return 0;
}
class Location{
private:
int x,y;
public:
void init(int x=0,int y=0);
void valueX(int val=0){x=val;}
int valueX(){return x;}
};
Location A;
A.valueX();//错误,编译器无法判断调用哪一个valueX
2.构造函数
成员函数的一种
-
名字与类名相同,可以有参数,不能有返回值(void也不行) -
作用是对对象进行初始化,如给成员变量赋初值 -
如果定义类时没有写构造函数,则编译器生成一个默认的无参数的构造函数 默认构造函数无参数,不做任何操作 -
如果定义了构造函数,则编译器不生成默认的无参数的构造函数 -
对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数。 -
一个类可以有多个构造函数
为什么需要有构造函数:
(1)构造函数执行必要的初始化工作,有了构造函数,就不必专门再写初始化函数,也不用担心忘记调用初始化函数。
(2)有时对象没有被初始化就使用,会导致程序出错
class Complex{
private:
double real,imag;
public:
void Set(double r,double i);
};//编译器自动生成默认构造函数
Complex c1;//默认构造函数被调用
Complex *pc=new Complex;//默认构造函数被调用
class Complex{
private:
double real,imag;
public:
Complex(double r,double i=0);
}
Complex::Complex(double r,double i){
real=r;imag=i;
}
Complex c1;//错误,缺少构造函数的参数
Complex *pc=new Complex;//错误,缺少参数
Complex c1(2);//正确,调用第一个
Complex c1(2,4),c2(3,5);
Complex *pc=new Complex(3,4);
可以有多个构造函数,参数个数或类型不同
class Complex{
private:double real,imag;
public:
void Set(double r,double i);
Complex(double r,double i);
Complex(double r);
Complex(Complex c1,Complex c2);
};
Complex::Complex(double r,double i){
real=r;imag=i;
}
Complex::Complex(double r){
real=r;imag=0;
}
Complex::Complex(Complex c1,Complex c2){
real=c1.real+c2.real;
imag=c1.imag+c2.imag;
}
Complex c1(3),c2(1,0),c3(c1,c2);
构造函数最好是public的,private构造函数不能直接用来初始化对象
class CSample{
private:CSample(){
}
};
int main(){
CSample Obj;//错误,唯一的构造函数是private
return 0;
}
习题:
有类A如下定义:
class A{
int v;
public:
A(int n){v=n;}
};
//下面哪条语句编译不会出错?
A a1(3); //正确
A a2; //错误
A * p=new A(); //错误
class CSample{
int x;
public:
CSample(){
cout<<"Constructor 1 called"<<endl;
}
CSample(int n){
x=n;
cout<<"Constructor 2 called"<<endl;
}
};
int main(){
CSample array1[2]; //constructor 1 called
//constructor 1 called
cout<<"step1"<<endl; //step 1
CSample array2[2]={4,5};//constructor 2 called
//constructor 2 called
cout<<"step2"<<endl; //step2
CSample array3[2]={3}; //constructor 2 called
//constructor 1 called
cout<<"step3"<<endl; //step3
CSample * array4=new CSample[2];
//constructor 1 called
//constructor 1 called
delete [] array4;
return 0;
}
class Test{
public:
Test(int n){} (1)
Test(int n,int m){} (2)
Test(){} (3)
};
Test array1[3]={1,Test(1,2)};//分别用(1)(2)(3)初始化
Test array2[3]={Test(2,3),Test(1,2),1};//分别用(2)(2)(1)初始化
Test *p Array[3]={new Test(4),new Test(1,2)};//前两个分别用(1),(2)初始化,后一个未初始化
习题:
假设A是一个类的名字,下面的语句生成了几个类A的对象?
A * arr[4]={new A(),NULL,new A()};
答案:两个
只有在建立对象的时候,才会初始化,NULL没有调用构造函数,不等于调用无参数构造函数,只是给A*类型指针赋值NULL。
|