结构和类既相似又有区别:
在C中,结构不能定义成员函数;而在C++中,结构可以定义成员函数。
在C++中,结构和类的不同点是:缺省状态下,类成员是私有的,而结构成员是公有的;相同点在于:两个结构变量或类对象可以相互赋值,也可以对结构变量或类对象逐个成员进行赋值。
定义类成员函数的方法:
<1>内联函数定义。
内联函数的定义方式有两种,隐式定义(在类声明中实现函数,不使用inline关键词)和显式定义(在类声明或类外实现,实现时使用inline关键词)。
内联函数的基本思想在于将每个函数调用用它的代码体来替换。一方面,如果内联函数体比较长,将增加类定义的规模和复杂性,很可能会增加整个目标代码的体积,成员函数的代码不能被该类所有的对象共享,系统要为每个对象创建一份拷贝,浪费了内存空间;另一方面,如果内联函数体非常短,编译器为这个函数体生成的代码就会真的比为函数调用生成的代码要小许多,内联这个函数将会确实带来更小的目标代码和更高的缓存命中率!
使用内联函数可缩短执行时间,但会增加程序长度,因此,inline关键词应只用于经常使用的小函数,编译器会忽略其他函数的inline关键词。
<2>外联函数定义(在类中声明成员函数,在类外实现成员函数,并且不使用inline关键词)。
C++编译器会为成员函数的指令创建唯一一份拷贝,供该类的任何对象共享,节省了内存空间。
在成员函数中,如果参数名和类成员变量一样时,参数名被使用,类成员名被隐藏。通过在成员变量名前添加类名和双冒号,或使用this指针来解决。
VoidCLSA::SetValue(intx, inty)
{
{
x=x; //set invalid
CLSA::y = y; //set valid
This->y = y; //set valid
}
在类成员函数中,可以使用this指针,也可以显式地传递this指针给另一个函数。
在构造函数中可以使用缺省参数值,当调用没有为函数指定参数时,使用缺省变元。缺省变元仅仅能放置在参数列的最后,也就是缺省变元后不能有其他参数。
classA {
A(intx, inty = 0); //ok
//A(int x =0, int y, int z = 0); //error
A(chara, intx = 0, inty = 0); //ok
}
voidmain()
{
Aa(3); //调用A(int x, int y = 0),传递一个参数
Ab(6, 9); //调用A(int x, int y = 0),传递两个参数
Ac('a'); //调用A(char a, int x = 0, int y = 0),仅传递一个参数
}
定义函数时,一个引用变量不能被定义为缺省参数。
初始化类成员的另一种方法如下,构造函数初始化类数据成员的顺序与数据成员在类中的声明顺序是一致的,与初始化类数据成员的排列顺序无关。
classCLSA
{
public:
CLSA();
intx,y;
}
CLSA::CLSA():x(4), y(x) {
}
结果:x、y都为4
|
classCLSA
{
public:
CLSA();
intx,y;
}
CLSA::CLSA():y(4), x(y) {
}
结果:y为4, x为系统分配数值
|
拷贝构造函数是一个特殊的构造函数,当创建一个新对象时,系统自动调用它;另外,它的功能在于将对象的参数列表逐域拷贝到新建的对象中。
拷贝构造函数的定义有两种形式:
<1>系统定义(缺省拷贝构造函数)
classpoint {
public:
point( intx, inty){
point::x = x;
this->y = y;
}
private:
intx, y;
};
voidmain()
{
pointp1(3, 4);
pointp2(p1); //缺省拷贝构造函数,将对象p1的各域逐个拷贝给对象p2
pointp3 = p1; //调用系统或用户定义的拷贝构造函数,将对象p1的各域逐个拷贝给对象p3
}
<2>用户定义
拷贝构造函数定义形式如下左表:
point:: point(constpoint &p)
{
point::x = p.x;
this->y = p.y;
}
|
point::point(constpoint *p)
{
point::x = (*p).x;
this->y = (*p).y;
}
|
拷贝构造函数的调用同上,这时程序调用用户定义的拷贝构造函数,而不是系统本身的拷贝构造函数。
但是,上表右侧函数不是用户自定义的拷贝构造函数。
在传递大型对象时,使用常量引用参数既能模拟传值调用的安全性,又能避免传递大型对象拷贝的开销。
类的静态成员可以是数据成员,也可以是成员函数。静态成员的特征是,不管该类创建了多少个对象,其静态成员只有一个副本,这个副本被属于这个类的所有对象共享。
当创建类的第一个对象时,类的所有静态数据成员被初始化,若有没有被初始化的静态数据成员,系统报错;再次建立对象时,不需要对静态数据成员进行初始化。类的所有对象共享类的静态数据成员,它不会随某个对象而改变。静态数据成员可以由静态成员函数访问,也可以由非静态成员函数访问。
静态类对象,在声明时调用构造函数一次,程序终止时调用析构函数。
静态成员函数属于整个类,而不属于类中的某个对象。静态成员函数仅能访问类中的静态数据成员或全局变量,不能访问非静态数据成员;静态成员函数仅能调用类中的静态成员函数和全局函数,不能掉用类中非静态成员函数。静态成员函数中没有this指针,无法判断当前对象是哪一个,也无法判断静态数据成员属于哪个对象,所以如果在函数中需要确定对象,一般采用参数传递对象的方法。在调用静态成员函数的前面,对象名和类名,必须缀上一个,否则是错误的。在不强调调用对象的前提下,使用对象名和类名是一样的。
注意:
用const去声明一个静态成员函数会导致编译错误。
(modifiers not allowed on static member functions
Error executing cl.exe.)
constchar * sys = "Win32"; //全局变量
classA {
public:
A(){
count++; };
staticintgetCount(void){
returncount; }
staticintgetCount(Aa){ //传递对象
returna.count; }
staticchar * getStr(){
return (char *)sys; }
private:
staticintcount;
};
intA::count = 0;
voidmain()
{
Aa;
intnum = a.getCount(); //前缀对象名a
num = A::getCount(); //前缀类名
num = A::getCount(a); //前缀A::
charstr[20];
memset(str, 0, 20);
strcpy(str, a.getStr());
}
某个类对象或指针可以成为另一个类的数据成员。在对类调用构造函数进行初始化时,如果需要传递参数给对象成员,也必须调用内部对象所在类的构造函数进行初始化。
classA {
intcount;
public:
A(inti){
count = i; }
};
classB {
intnumber; Aa; //类对象为成员变量
public:
B(inti);
};
B::B(inti):a(i){ // 不能写成 B::B(inti):A(i)或B::B(inti):a(int j)
number = i; }
voidmain()
{
Bb(100);
}
1.6 const 对象和const 成员函数
注意:
1 const成员函数不能保证,在调用成员函数期间类对象引用的所有东西都保持不变。例如,类中如果含有指针,那么在const成员函数中就能修改指针所指的对象。
#include <cstring>
class Text {
public:
void bad( const string & ) const;
private:
char *_text;
};
void Test::bad( const string &parm ) const
{
_text = parm.c_str(); //错误:不能修改_text
for ( int ix = 0; ix < parm.size(); ++ix)
_text[ix] = parm [ix]; //不好的风格,但不是错误的
}