三天学会c++ 之二
-
结构与类
结构与类的主要区别是:默认访问级别不同,结构默认是public,类默认是private。
C++中,结构的成员(数据和函数)通常分两类:
私有成员:private,它只能被该结构中的其它成员访问;
公有成员:public,既可以被结构中的其它成员访问,又可以被结构外的其它部分访问。
类使用注意:
类的声明中的private和public两个关键字可以按任意顺序出现任意次。为了使程序更加清晰,把所有私有成员和公有成员归类放在一起。
除了private和public之外,还有protected(保护性成员)关键字。
数据成员可以是任何数据类型,但不能用auto、register或extern说明。
不能在类的声明中给数据成员进行初始化。
定义成员函数的方式:
-
-
在类声明中只给出成员函数的原型,其具体定义在类的外部 一般格式为
返回类型 类名::函数名(参数表)
{
//函数体
} 例: //成员函数的外部定义
#include <iostream.h> -
class point
{
private:
int x,y;
public:
void setpoint(int,int); //设置坐标点的成员函数原型
int getx(); //取x坐标点的成员函数原型
int gety(); //取y坐标点的成员函数原型
}; -
inline void point::setpoint(int a,int b) //定义成员函数setpoint()
{
x=a; y=b;
} -
int point::getx() //定义成员函数getx()
{
return x;
} -
int point::gety() //定义成员函数gety()
{
return y;
}
-
-
成员函数定义为“内联函数”:
-
-
-
隐式定义:直接书写在类中。
-
显式定义:类中给出成员函数声明,在类外定义时,冠以inline。
-
-
例:
//“类的内联函数”
//隐式内联
class point
{
private:
int x,y;
public:
void setpoint(int,int) //定义设置坐标点的成员函数
{
x=a;
y=b;
}
int getx() //定义取x坐标点的成员函数
{
return x;
}
int gety() //定义取y坐标点的成员函数
{
return y;
}
};
////////////////////////////////////////////////
//显式内联
class point
{
private:
int x,y;
public:
void setpoint(int,int); //定义设置坐标点的成员函数
int getx(); //定义取x坐标点的成员函数
int gety(); //定义取y坐标点的成员函数
};
inline void point::setpoint(int,int); //定义设置坐标点的成员函数
{
x=a;
y=b;
}
inline int point::getx() //定义取x坐标点的成员函数
{
return x;
}
inline int point::gety() //定义取y坐标点的成员函数
{
return y;
}
类与对象的关系
-
-
类—数据类型 对象(实例)—变量
-
对象的定义
-
-
声明类的同时定义对象:这是全局的对象。
-
在使用对象时再定义:这是局部对象。
-
说明:
-
定义对象之后,会为对象分配存储空间。
-
全局的对象在任何函数内均可使用,而局部对象只能在定义对象的函数中使用。
-
-
对象的引用
是指对对象成员的引用。不论是数据成员,还是成员函数,只要是公有的,就可以被外部函数直接引用:
-
-
对象名.数据成员名
-
对象名.成员函数名(实参表)
-
说明:
-
-
op1.setpoint(1,2)实际上是op1.point::setpoint(1,2)的缩写形式,两者等价。
-
外部函数不能引用对象的私有成员。如:
-
void main()
{
point op;
op.x=5; //错误
…
}
-
-
在定义对象指针时,使用指针运算符“->”访问对象成员。如:
-
void main()
{
point a;
point *op=&a;
op->setpoint(1,2); //正确 op.setpoint(1,2); //错误
…
}
对象赋值语句
同一种类的对象可以相互赋值。如:
point op1,op2;
op1.setpoint(1,2);
op2=op1;
此时是将所有数据成员逐个地进行拷贝。则:
cout<<op2.getx(); //结果为1
说明:
1)在使用对象赋值时,两个对象的类型必须相同。
2)两个对象之间的赋值,仅仅使这些对象中的数据相同,而两个对象仍是独立的对象,它们有各自的数据成员。
3)将一个对象的值赋给另一个对象时,多数情况下都是成功的,但当类中存在指针时,可能会产生错误。
类的作用域
-
类的作用域就是指在类声明中一对花括号所形成的作用域。一个类的所有成员都在该类的作用域内。一个类的任何成员可以引用该类的其它成员。C++把类的所有成员都作为一个整体的相关部分。
-
一个类的成员函数可以不受限制地引用该类的数据成员,而在该类作用域之外,对该类的数据成员和成员函数的引用则要受到一定的限制,只有公有成员才可被外部函数引用,而对私有成员是不允许被外部函数引用的。这体现了类的封装功能
构造函数与析构函数
-
当声明一个类对象时,编译程序需要为对象分配存储空间,进行必要的初始化,这部分工作随着类的不同而不同。在C++中,由构造函数来完成这些工作。
-
构造函数是属于某一个类的,它可以由用户提供,也可以由系统自动生成。
-
与构造函数对应的是析构函数,当撤消类对象时,析构函数就回收存储空间,并做一些善后工作。析构函数也属于某一个类,它可以由用户提供,也可以由系统自动生成。
-
构造函数是一种特殊的成员函数,它主要用于为对象分配空间,进行初始化。
-
构造函数具有一些特殊的性质。
-
构造函数的名字必须与类名相同。
-
定义对象时被系统自动调用。
-
可以有任意类型的参数,但不能具有返回值。
-
被定义为公有的,但其它时间无法被调用。
-
例:
class complex
{
private:
double real, imag;
public:
complex(double r,double i)
{ real=r; imag=i; }
double abscomplex()
{
double t;
t=real*real+imag*imag;
return sqrt(t);
}
};
void main()
{
complex A(1.1,2.2); //定义类的对象A时调用构造函数complex
cout<<” abs of complex A=”<<A.abscomplex()<<endl;
}
注意:
-
-
构造函数的名字必须与类名相同,否则将被当作一般的成员函数来处理。
-
构造函数没有返回值,是不能说明其返回类型的,即使为void也不行。
-
每一个类都必须有一个构造函数。如果没有给类定义构造函数,则编译系统自动地生成一个缺省的构造函数。形如:
-
complex::complex(){}
构造函数不能像其它成员函数那样被显式地调用,它是在定义对象的同时调用的。
-
-
构造函数可以是不带参数的。
-
构造函数也可采用构造初始化表对简单的数据成员进行初始化,但对数组的初始化应在构造函数体中书写。
-
对没有定义的构造函数的类,其公有数据成员可以用初始化表进行初始化。
-
例:
//无构造函数的类,对其公有数据成员用
//初始化表进行初始化。
#include <iostream.h>
#include <string.h>
class myclass1{
public:
char name[10];
int no;
myclass1(char *s,int n)
{
strcpy(name,s);
no=n;
}
};
class myclass2{
public:
char name[10];
int no;
};
void main()
{
myclass1 a("chen",25);
myclass2 b={"chang",12};
cout<<a.name<<' '<<a.no<<endl;
cout<<b.name<<' '<<b.no<<' '<<endl;
}
-
缺省参数的构造函数
对于带参数的构造函数,在定义对象时必须给构造函数传递参数,否则构造函数将不被执行。但在实际使用中,有些构造函数的参数值通常是不变的,只有在特殊情况下才需要改变它的参数值,这时可以将其定义成带缺省参数的构造函数。
-
重载构造函数
与一般的成员函数一样,C++允许重载构造函数,以适应不同的场合。这些构造函数之间以它们所带参数的个数或类型的不同来加以区分。
说明:
在重载没有参数和缺省参数的构造函数时,有可能产生二义性。
例:
//构造函数重载
#include <iostream.h>
#include <stdlib.h>
class timer
{
int seconds;
public:
// timer()
{
seconds=5;
} //无参构造函数
timer(char *t) //含有一个数字串参数的构造函数
{
seconds=atoi(t);
}
timer(int t) //含一个整型参数的构造函数
{
seconds=t;
}
timer(int min=0,int sec=0)//含两个整型参数的构造函数
{
seconds=min*60+sec;
}
int gettime()
{
return seconds;
}
};
void main()
{
timer a; //缺省调用timer(int t)
// timer b(10); //出错,重载存在二义性
timer c("20");
timer d(1,10);
cout<<"seconds1="<<a.gettime()<<endl;
// cout<<"seconds2="<<b.gettime()<<endl;
cout<<"seconds3="<<c.gettime()<<endl;
cout<<"seconds4="<<d.gettime()<<endl;
}
析构函数
析构函数也是一种特殊的成员函数.它执行与构造函数相反的操作,通常用于执行一些清理任务,如释放分配给对象的内存空间等。
析构函数有以下一些特点:
-
-
析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);
-
析构函数没有参数,也没有返回值,而且不能重载,因此在一个类中只能有一个析构函数;
-
当撤消对象时,编译系统会自动地调用析构函数
-
例:
class complex
{
double real, imag;
public:
complex(double r=0.0,double i=0.0)
{
cout<<"construction…"<<endl;
real=r; imag=i;
}
~complex()
{
cout<<"destruction…"<<endl;
}
double abscomplex()
{
return sqrt(real*real+imag*imag);
}
};
void main()
{
complex A(1.1,2.2);
cout<<"abs of complex A="<<A.abscomplex()<<endl;
}
说明:
-
每个类必须有一个析构函数。若没有显式地为一个类定义析构函数,编译系统会自动地生成一个缺省的析构函数。如:
~complex(){}
-
对于大多数类而言,缺省的析构函数已经足够了。但是,如果在一个对象完成其操作之前需要做一些内部处理,则应该显式地定义析构函数,例如:
class string_data
{
private: char *str;
public:
string_data(char *s)
{
str=new char[strlen(s)+1];
strcpy(str,s);
}
~string_data()
{
delete str;
}
//…
};
-
拷贝构造函数
拷贝构造函数是一种特殊的构造函数。它用于依据已存在的对象建立一个新对象。典型的情况是,将参数代表的对象逐域拷贝到新创建的对象中。
用户可以根据自己的需要定义拷贝构造函数,系统也可以为类产生一个缺省的拷贝构造函数。
1、自定义拷贝构造函数
classname(const classname &ob)
{
//拷贝构造函数的函数体
}
其中ob是用来初始另一个对象的对象的引用。
例:
class point
{
int x,y;
public:
point(int a,int b) //构造函数
{
x=a; y=b;
}
point(const point &p) //拷贝构造函数,若省去,系统则会缺省的创建
{
x=p.x;
y=p.y;
}
//……
};
void main()
{
point p1(30,40); //定义对象p1
point p2(p1); //显式调用拷贝构造函数,通过对象p1创建对象p2
……
}
-
缺省的拷贝构造函数
如果没有编写自己定义的拷贝构造函数,C++会自动地将一个已存在的对象复制到新对象,这种按成员逐一复制的过程是由缺省的拷贝构造函数自动完成的。
说明:
-
-
与一般的构造函数一样,拷贝构造函数没有返回值。
-
通常缺省的拷贝构造函数是能够胜任工作的,但若类中有指针类型时,按成员复制的方法有时会产生错误:
-
当类中有指针类型时,正确的处理方法是增加自己的拷贝构造函数:
class string_data
{
private: char *str;
public:
string_data(char *s)
{
str=new char[strlen(s)+1];
strcpy(str,s);
}
string_data(const string_data &p)
{
str=new char[strlen(p.str)+1];
strcpy(str,p.str);
}
~string_data()
{
delete str;
}
//…
};
void main()
{
string_data x(“abc”);
string_data y(x);
}
●类与对象的高级应用
对象数组
对象数组是指每一数组元素都是对象的数组,也就是说,若一个类有若干个对象,我们把这一系列的对象用一个数组来存放。
对象数组由C++的系统缺省构造函数建立。
-
如果类中含有用户定义的构造函数,而且构造函数带有参数,则定义对象数组时,可通过初始值表进行赋值。
-
若类中又同时含有不带参数的构造函数,那么定义对象数组时,也可通过不带参数的构造函数或带有缺省参数的构造函数给对象数组元素赋值。
例:
// 定义对象数组时,通过初始化表调用
// 带有参数的构造函数。
#include <iostream.h>
class exam
{
int x;
public:
exam(int a=5)
{
x=a;
}
int get_x()
{
return x;
}
};
void main()
{
exam ob[4];//={11,12,21,22}; //初始化表
int k;
for(k=0;k<4;k++)
cout<<ob[k].get_x()<<' ';
cout<<endl;
}
对象指针
在C语言中能够直接访问结构,或通过指向该结构的指针来访问结构。类似地,在C++语句中可以直接引用对象,也可以通过指向该对象的指针引用对象。对象指针是C++的重要特性之一。
用指针引用单个对象成员
-
-
说明对象指针的语法和说明其它数据类型指针的语法相同。如:
-
int *p; point *q;
-
-
使用对象指针时,首先要把它指向一个已创建的对象,然后才能引用该对象的成员。 如:
-
p=&x; q=&op; 或 point *p=&a;
-
-
一般情况下,用点运算符来引用对象成员,当用指向对象的指针来引用对象成员时,就要用“->”操作符。如:
-
op.setpoint(1,2); q->setpoint(1,2);
对象指针不仅能引用单个对象,也能引用对象数组。
this指针
C++提供了一个特殊的对象指针——this指针。它是成员函数所属对象的指针,它指向类对象的地址。成员函数通过这个指针可以知道自己属于哪一个对象。this指针是一种隐含指针,它隐含于每个类的成员函数中。
例:
class exam
{
int x;
public:
void load(int val)
{
this->x=val;
} //与x=val等价
int getx()
{
return this->x;
}//与return x;等价
};
void main()
{
exam ob,ob1;
ob.load(100);
cout<<ob.getx();
}
1、当形式参数与数据成员同名时,需要加this:
class ABC
{
public:
int x;
void set(int x)
{
this->x = x;
} //形参与数据成员同名
}
C++对变量的检查顺序:局部变量、类的数据成员、全局变量。
2、函数返回调用该函数的对象的引用
例如在类中,有设置函数set()和显示函数show()
理想情况下,希望能够将一些操作序列连接成一个单独的表达式:
如: a.set(13,23).show();
等价于:
a.set(13,23);
a.show();
例:
//this 指针使用2
#include <iostream.h>
class ABC
{
int x;
public:
ABC &set(int x) //形参与数据成员同名
{
this->x = x;
return *this;
}
void show()
{
cout<<x<<endl;
}
};
void main()
{
ABC p;
p.set(5).show();
// p.set(6);
// p.show();
}
向函数传递对象
同类对象之间可以实现赋值操作,如:
ABC ob1,ob2;
…… //对ob1实现初始化代码
ob2=ob1;
这就提供了对象可以作参数的条件。如:
fun1(ABC p) {……}
fun2(ABC *p) {……}
fun3(ABC &p){……}
void main()
{ ABC p1,p2,p3;
……
fun1(p1); //以“值方式”传递对象,p1不被修改
fun2(&p2); //以“指针方式”传递对象,p2被修改
fun3(p3); //以“引用方式”传递对象,p3被修改
}
-
值方式
“值方式”传递对象:
class tr
{
int i;
public:
tr(int n)
{
i=n;
}
void set(int n)
{
i=n;
}
int get()
{
return i;
}
};
void Sqr(tr ob)
{
ob.set(ob.get()*ob.get());
cout<<"ob.i="<<ob.get()<<endl; // i = 100;
}
void main()
{
tr obj(10);
Sqr(obj);
cout<<"obj.i="<<obj.get()<<endl; // i = 10;
}
-
指针方式传递参数
class tr
{
int i;
public:
tr(int n)
{
i=n;
}
void set(int n)
{
i=n;
}
int get(){return i;}
};
void Sqr(tr *ob)
{
ob->set(ob->get()*ob->get());
cout<<"ob.i="<<ob->get()<<endl; // i = 100;
}
void main()
{
tr obj(10);
Sqr(&obj);
cout<<"obj.i="<<obj.get()<<endl; // i = 100;
}
3、引用方式
class tr
{
int i;
public:
tr(int n)
{
i=n;
}
void set(int n)
{
i=n;
}
int get()
{
return i;
}
};
void Sqr(tr &ob)
{
ob.set(ob.get()*ob.get());
cout<<"ob.i="<<ob.get()<<endl;
}
void main()
{
tr obj(10);
Sqr(obj);
cout<<"obj.i="<<obj.get()<<endl;
}
静态成员
-
在类定义中,它的成员(包括数据成员和成员函数)可以用关键字static声明为静态的,这些成员称为静态成员。
-
静态成员的特性:
-
不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。
-
-
静态成员包括:
-
静态数据成员
-
静态成员函数
-
-
在一个类中,若将一个数据成员声名为static,这种成员称为静态数据成员。(静态数据成员在类外定义,在类内声名)
-
与一般的数据成员不同,无论建立了多少个对象,都只有一个静态数据的拷贝。
-
可以认为该静态数据是属于该类的,而不是具体的属于某一个对象。
1、静态数据成员
说明:
-
静态数据成员属于类(准确地说,是属于类中一个对象集合),而不像普通数据成员那样属于某一对象,因此可以使用“类名::”访问静态的数据成员。
-
静态数据成员不能在类中进行初始化,因为在类中不给它分配内存空间,必须在类外的其它地方为它提供定义。一般在main() 开始之前、类的声明之后的特殊地带为它提供定义和初始化。缺省时,静态成员被初始为0。
-
静态数据成员与静态变量一样,是在编译时创建并初始化。它在该类的任何对象被建立之前就存在,它可以在程序内部不依赖于任何对象被访问。
-
C++支持静态数据成员的一个主要原因是可以不必使用全局变量。依赖于全局变量的类几乎都是违反面向对象程序设计的封装原理的。静态数据成员的主要用途是定义类的各个对象所公用的数据,如统计总数、平均数等。
2、静态成员函数
-
在类定义中,前面有static说明的成员函数称为静态成员函数。
-
静态成员函数首先是一个成员函数,因此它不能像普通函数那样使用。在使用时要用“类名::”作为它的限定词,即可对它进行调用。
-
其次,静态成员函数是一种特殊的成员函数,它不属于某一个特定的对象。一般而言,静态成员函访问的基本上是静态数据成员或全局变量。
例:
// 使用静态成员函数访问静态数据成员
#include <iostream.h>
class small_cat
{
private:
double weight;
static double total_weight; //静态数据成员
static double total_number; //静态数据成员
public:
small_cat(double w)
{
weight=w;
total_weight+=w;
total_number++;
}
void display()
{
cout<<"小猫的重量是:"<<weight<<"磅"<<endl;
}
static void total_disp() //静态成员函数
{
cout<<total_number<<"只小猫总重是:"<<total_weight<<"磅"<<endl;
}
};
double small_cat::total_weight=0;
double small_cat::total_number=0;
void main()
{
small_cat w1(1.8),w2(1.6),w3(1.5);
w1.display();
w2.display();
w3.display();
small_cat::total_disp(); //通过类调用静态成员函数
w1.total_disp();
w2.total_disp();
w3.total_disp();
}
说明:
-
静态成员函数可以定义成内嵌的,也可以在类外定义,在类定义时,不要用static前缀。
-
编译系统将静态成员函数限定为内部连接,也就是说,与现行文件相连接的其它文件中的同名函数不会与该函数发生冲突,维护了该函数使用的安全性,这是使用静态成员函数的一个原因。
-
使用静态成员函数的另一个原因是,可以用它在建立任何对象之前处理静态数据成员,这是普通成员函数不能实现的功能。
-
在一般的成员函数中都隐含有一个this指针,用来指向对象自身,而在静态成员函数中没有this指针,因为它不与特定的对象相联系,调用时使用:
类名::静态成员函数名()
如:small_cat::total_disp(),当然使用:
对象.静态成员函数名()
也是正确的。如:w1.total_disp()
5.一般而言,静态成员函数不能访问类中的非静态成员。
●友元
-
类的主要特点之一是数据隐藏,即类的私有成员只能在类定义的范围内使用,也就是说私有成员只能通过它的成员函数来访问。
-
但是,有时候需要在类的外部访问类的私有成员。为此,就需要寻找一种途径,在不放弃私有数据安全性的情况下,使得类外部的函数或类能够访问类中的私有成员,在C++中就用友元作为实现这个要求的辅助手段。
-
C++中的友元为数据隐藏这堵不透明的墙开了一个小孔,外界可通过这个小孔窥视类内部的秘密,友元是一扇通向私有成员的后门。
-
友元可分为:友元函数,友元成员,友元类。
友元函数
-
友元函数不是当前类的成员函数,而是独立于当前类的外部函数,但它可以访问该类的所有对象的成员,包括私有成员和公有成员。
-
在类定义中声明友元函数时,需在其函数名前加上关键字friend。此声明可以放在公有部分,也可以放在私有部分。友元函数可以定义在类的内部,也可以定义在类的外部。
例:
// 使用友元函数
#include <iostream.h>
#include <string.h>
class girl
{
private:
char *name;
int age;
public:
girl(char *n,int d)
{
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
friend void disp(girl &); //声明为友元函数
~girl()
{
delete name;
}
};
void disp(girl &x) //定义友元函数
{
cout<<"girl/'s name is:"<<x.name<<",age:"<<x.age<<endl;
}
void main()
{
girl e("Chen Xingwei",18);
disp(e); //调用友元函数
}
说明:
-
友元函数虽然可以访问类对象的私有成员,但它毕竟不是成员函数。因此,在类的外部定义友元函数时,不必像成员函数那样,在函数名前加上“类名::”。
-
友元函数一般带有一个该类的入口参数。因为友元函数不是类的成员,所以它不能直接引用对象成员的名称,也不能通过this指针引用对象的成员,它必须通过作为入口参数传递进来的对象名或对象指针来引用该对象的成员。
-
当一个函数需要访问多个类时,友元函数非常有用,普通的成员函数只能访问其所属的类,但是多个类的友元函数能够访问相应的所有类的数据。
例:
//使用一个友元函数访问两个不同的类
#include <iostream.h>
#include <string.h>
class boy; //向前引用
class girl
{
char name[25];
int age;
public:
void init(char N[],int A);
friend void prdata(const girl plg,const boy plb); //声明函数为girl类的友元函数
};
void girl::init(char N[],int A)
{
strcpy(name,N);
age=A;
}
class boy
{
char name[25];
int age;
public:
void init(char N[],int A);
friend void prdata(const girl plg,const boy plb); //声明函数为boy类的友元函数
};
void boy::init(char N[],int A)
{
strcpy(name,N);
age=A;
}
void prdata(const girl plg,const boy plb)
{
cout<<"女孩"<<plg.name<<','<<plg.age<<"岁;";
cout<<"男孩"<<plb.name<<','<<plb.age<<"岁。/n";
}
void main()
{
girl G1,G2,G3;
boy B1,B2,B3;
G1.init("Stacy",12);
G2.init("Judith",13);
G3.init("Leah",12);
B1.init("Jim",11);
B2.init("Micheal",13);
B3.init("Larry",12);
prdata(G1,B1);
prdata(G2,B2);
prdata(G3,B3);
}
-
友元函数通过直接访问对象的私有成员,提高了程序运行的效率。在某些情况下,如运算符被重载时,需要用到友元。但是友元函数破坏了数据的隐蔽性,降低了程序的可维护性,这与面向对象的程序设计思想是背道而驰的,因此使用友元函数应谨慎。
友元成员
-
除了一般的函数可以作为某个类的友元外,一个类的成员函数也可以作为另一个类的友元,这种成员函数不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访问friend声明语句所在类对象中的私有成员和公有成员,这样能使两个类相互合作、协调工作,完成某一任务。
例:
// 使用友元成员函数访问另一个类
#include <iostream.h>
#include <string.h>
class girl; //向前引用
class boy
{
char *name;
int age;
public:
boy(char *N,int A)
{
name=new char[strlen(N)+1];
strcpy(name,N);
age=A;
}
void disp(girl &); //声明disp()为类boy的成员函数
~boy()
{
delete name;
}
};
class girl
{
char *name;
int age;
public:
girl(char *N,int A)
{
name=new char[strlen(N)+1];
strcpy(name,N);
age=A;
}
friend void boy::disp(girl &); //声明类boy的成员函数disp()为类girl的友元函数
~girl()
{
delete name;
}
};
void boy::disp(girl &x)
{
cout<<"boy/'s name is:"<<name<<",age:"<<age<<endl; //访问本类对象成员
cout<<"girl/'s name is:"<<x.name<<",age:"<<x.age<<endl; //访问友元类对象成员
}
void main()
{
boy b("chen hao",25);
girl g("zhang wei",18);
b.disp(g);
}
说明:
-
-
一个类的成员函数作为另一个类的友元函数时,必须先定义这个类。例如上例中,类boy的成员函数为类girl的友元函数,必须先定义类boy。并且在声明友元函数时,要加上成员函数所在类的类名,如:
-
friend void boy::disp(girl &);
-
-
程序中还要使用“向前引用”,因为函数disp()中将girl &作为参数,而girl要晚一些时候才定义。
-
友元类
-
不仅函数可以作为一个类的友元,一个类也可以作为另一个类的友元。这种友元类的说明方法是在另一个类声明中加入语句“friend 类名;”,其中的“类名”即为友元类的类名。此语句可以放在公有部分也可以放在私有部分,例如:
class Y{
//……
};
class X{
//……
friend Y;
//……
};
-
当一个类被说明为另一个类的友元时,它的所有的成员函数都成为另一个类的友元函数,这就意味着为友元的类中的所有成员函数都可以访问另一个类的私有成员。
例:
//boy为girl的友元类
#include <iostream.h>
#include <string.h>
class girl;
class boy
{
char *name;
int age;
public:
boy(char *n,int d)
{
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
void disp(girl &); //声明disp()为类boy的成员函数
~boy()
{
delete name;
}
};
class girl
{
char *name;
int age;
friend boy; //声明类boy是类girl的友元
public:
girl(char *n,int d)
{
name=new char[strlen(n)+1];
strcpy(name,n);
age=d;
}
~girl()
{
delete name;
}
};
void boy::disp(girl &x) //定义函数disp()为类boy的成员函数,也是类girl的友元函数
{
cout<<"boy/'s name is:"<<name<<",age:"<<age<<endl;
cout<<"girl/'s name is:"<<x.name<<",age:"<<x.age<<endl;
}
void main()
{
boy b("chen hao",25);
girl g("zhang wei",18);
b.disp(g);
}
说明:
-
友元关系是单向的,不具有交换性(我是你的朋友,不能推断出:你是我的朋友)。
-
友元关系也不具有传递性(我是你的朋友,你是他的朋友,不能推断出:我是他的朋友)。
类对象作为成员
-
在类定义中定义的数据成员一般都是基本的数据类型。但是类中的成员也可以是对象,叫做对象成员。使用对象成员着重要注意的问题是构造函数的定义方式,即类内部对象的初始化问题。
-
含有对象成员的类,其构造函数和不含对象成员的构造函数有所不同,例如有以下的类:
class X
{ 类名1 成员名1;
类名2 成员名2;
……
};
-
一般来说,类X的构造函数的定义形式为:
X::X(参数表0) : 成员名1(参数表1),……,成员名n(参数n表)
{ //构造函数体
}
-
当调用构造函数X::X()时,首先按各对象成员在类定义中的顺序依次调用它们的构造函数,对这些对象初始化,最后再执行X::X()的函数体。
-
析构函数的调用顺序与此相反。
例:
//对象作为类的数据成员
#include <iostream.h>
#include <string.h>
class string
{
private:
char *str;
public:
string(char *s)
{
str=new char[strlen(s)+1];
strcpy(str,s);
cout<<"构造string/n";
}
void print()
{
cout<<str<<endl;
}
~string()
{
cout<<"析构string/n";
delete str;
}
};
class girl
{
private:
string name;
int age;
public:
girl(char *st,int ag):name(st)
{
age=ag;
cout<<"构造girl/n";
}
void print()
{
name.print();
cout<<"age:"<<age<<endl;
}
~girl()
{
cout<<"析构girl/n";
}
};
void main()
{
girl obj("Chen hao",25);
obj.print();
}
说明:
-
声明一个含有对象成员的类,首先要创建各成员对象。本例在声明类girl中,定义了对象成员:string name。
-
girl类对象在调用构造函数进行初始化的同时,也要对对象成员进行初始化,因为它也是属于此类的成员。因此在写类girl的构造函数时,也缀上了对对象成员的初始化:
girl(char *st,int ag):name(st)
于是在调用girl的构造函数进行初始化时,也给对象成员name赋上了初值。
这里需要注意的是:在定义类girl的构造函数时,必须缀上其对象成员的名字name,而不能缀上类名,即:
girl(char *st,int ag):string(st)
是错误的,因为在类girl中是类string的对象name作为成员,而不是类string作为其成员。
本文详细介绍了C++中类与对象的概念、构造函数、析构函数、静态成员及友元等内容,帮助读者理解类与对象的基本用法及高级特性。
1964

被折叠的 条评论
为什么被折叠?



