Lecture 03 对象与类
类
对象构成了面向对象程序的基本计算单位,而对象的特征则由相应的类来描述。
对象是用类来创建的,因此,程序中首先要定义类。
C++的类是一种用户自定义类型,定义形式如下:
class <类名> { <成员描述> } ;
- 成员包括:数据成员和成员函数。
- 类成员标识符的作用域为整个类定义范围。
数据成员
数据成员是对类的对象所包含的数据描述,它们可以是常量和变量,类型可以是任意的C++类型(void除外) 。
数据成员的描述格式与C的结构成员的描述格式相同,例如:
class Date {//类定义
......
private: //访问控制说明
int year,month,day; //数据成员描述
};
在C++旧标准中,描述数据成员时不允许进行初始化(某些静态数据成员除外)。例如:
class A {
int x=0; //Error
const double y=0.0; //Error
......
};
成员函数
成员函数是对类定义中的数据成员所能实施的操作描述。
成员函数的实现(函数体)可以放在类定义中,例如:
class A
{ ...
void f() {...} //建议编译器按内联函数处理
};
成员函数的实现也可以放在类定义外,例如:
class A
{ ...
void f(); //声明
};
void A::f() { ... } //要用类名受限,区别于非成员函数(全局函数)
注意:在C++中,也允许在结构(struct
)和联合(union
)中定义函数,但成员的访问控制与类不同!
类成员函数名是可以重载的(析构函数除外),它遵循一般函数名的重载规则。例如:
class A {
......
public:
void f();
int f(int i);
double f(double d);
......
};
类成员的访问控制
在C++的类定义中,可以用访问控制修饰符public、private或protected来控制在类的外部对类成员的访问限制。例如:
class A
{ public: //访问不受限制。
int x;
void f();
private: //只能在本类和友元的代码中访问。
int y;
void g();
protected: //只能在本类、派生类和友元的代码中访问。
int z;
void h();
};
在C++的类定义中,可以有多个public、private和protected访问控制说明;默认访问控制是private。例如:
class A
{ int m,n; //m,n的访问控制为private。
public:
int x;
void f();
private:
int y;
void g();
protected:
int z;
void h();
public:
void f1();
};
一般来说,类的数据成员和在类的内部使用的成员函数应该指定为private,只有提供给外界使用的成员函数才指定为public。
- 具有public访问控制的成员构成了类与外界的一种接口(interface)。
- 在一个类的外部只能访问该类接口中的成员。
protected类成员访问控制具有特殊的作用(在派生类中使用)。
对象
类属于类型范畴的程序实体,它一般存在于静态的程序(编译程序看到的)中。
而动态(运行中)的面向对象程序则是由对象构成。
对象在程序运行时创建。
对象的创建和标识
直接方式
通过在程序中定义一个类型为类的变量来实现的。例如:
class A {
public:
void f();
void g();
private:
int x,y;
}
......
A a1; //创建一个A类的对象。
A a2[100]; //创建100个A类对象。
对象在进入相应变量的生存期时创建,通过变量名来标识和访问。相应变量的生存期结束时,对象消亡。
分为:全局对象、局部对象和成员对象。
间接方式(动态对象)
在程序运行时刻,通过用new操作来创建对象,用delete操作来撤消(使之消亡)。对象通过指针来标识和访问。
单个动态对象的创建与撤消
A *p;
p = new A; // 创建一个A类的动态对象。
… *p … //或,p->...,通过p访问动态对象
delete p; // 撤消p所指向的动态对象。
动态对象数组的创建与撤消
A *q;
q = new A[100]; //创建一个动态对象数组。
...q[i]... //或,*(q+i),访问动态对象数组中的第i个对象
delete []q; //撤消q所指向的动态对象数组。
成员对象
对于类的数据成员,其类型可以是另一个类。即,一个对象可以包含另一个对象(称为成员对象)。例如:
class A
{ ...
};
class B
{ ...
A a; //成员对象
...
};
B b; //对象b包含一个成员对象:b.a
成员对象跟随包含它的对象一起创建。
在类中说明一个数据成员的类型时,如果未见到相应类型的定义,或相应的类型未定义完,则该数据成员的类型只能是这些类型的指针或引用类型。例如:
class A; //A是在程序其它地方定义的类,这里是声明。
class B
{ A a; //Error,未见A的定义。
B b; //Error,B还未定义完。
A *p; //OK
B *q; //OK
A &aa; //OK
B &bb; //OK
};
对象的操作
对于创建的一个对象,需要通过向它发送消息(调用对象类中定义的某个public成员函数)来对它进行操作。例如:
class A
{ int x;
public:
void f();
};
int main()
{ A a; //创建A类的一个局部对象a。
a.f(); //调用A类的成员函数f对对象a进行操作。
A *p=new A; //创建A类的一个动态对象,p指向之。
p->f(); //调用A类的成员函数f对p所指向的对象进行操作。
delete p;
return 0;
}
在类的外部,通过对象来访问类的成员时要受到类成员访问控制的限制,例如:
class A
{ public:
void f()
{ ...... //允许访问:x,y,f,g,h
}
private:
int x;
void g()
{ ...... //允许访问:x,y,f,g,h
}
protected:
int y;
void h();
};
void A::f() //或A::g、A::h
{ ...... //允许访问:x,y,f,g,h
A a;
...//能访问a.x、a.y、a.g和a.h
}
void func()
{ A a;
a.f(); //OK
a.x = 1; //Error
a.g(); //Error
a.y = 1; //Error
a.h(); //Error
......
}
可以对同类对象进行赋值
Date yesterday,today,some_day;
some_day = yesterday; //默认是把对象yesterday的
//数据成员分别赋值给对象some_day的相应数据成员
取对象地址
Date today;
Date *p_date;
p_date = &today; //把对象today的地址赋值给指针p_date
把对象作为参数传给函数。例如:
void f(Date d) //创建一个新对象d,其数据成员用实参对象的数据成员对其初始化
{ ...... }
void g(Date &d) //不创建新对象,d就是实参对象!
{ ...... }
Date today;
f(today); //调用函数f,对象today不会被f修改
g(today); //调用函数g,对象today会被g修改!
把对象作为函数的返回值。例如,
Date f(Date &d)
{ d.print(); //输出:2020.2.20
return d; //创建一个临时对象作为返回值,用d对其初始化
}
Date& g(Date &d)
{ d.print(); //输出:2020.2.20
return d; //不创建新对象,把对象d作为返回值
}
Date some_day; //创建一个日期对象
some_day.set(2020,2,20);
f(some_day).set(2017,3,13); some_day.print() //?
g(some_day).set(2017,3,13); some_day.print() //?
//前者显式:2020.2.20,因为修改的是临时对象
//后者显式:2017.3.13,因为修改的是some_day!