友元函数是一种定义在类外部的普通函数,其特点是能够访问类中私有成员和保护成员,即类的访问权限的限制对其不起作用。
友元函数需要在类体内进行说明,在前面加上关键字friend。
一般格式为:
friend FuncName(<args>);
友元函数不是成员函数,用法也与普通的函数完全一致,只不过它能访问类中所有的数据。友元函数破坏了类的封装性和隐蔽性,使得非成员函数可以访问类的私有成员。
cout<
成员函数的调用,利用对象名调用
cout<
友元函数的调用,直接调用
友元函数不是类的成员函数
友元函数近似于普通的函数,它不带有this指针,因此必须将对象名或对象的引用作为友元函数的参数,这样才能访问到对象的成员。
友元函数与一般函数的不同点在于:
多态性:调用同一个函数名,可以根据需要但实现不同的功能。
可以将一个派生类对象的地址赋给基类的指针变量。
Base *basep;//基类指针
basep只能引用从基类继承来的成员
若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为虚函数
可以在程序运行时通过调用相同的函数名而实现不同功能的函数称为虚函数。定义格式为:
virtualFuncName(<ArgList>);
一旦把基类的成员函数定义为虚函数,由基类所派生出来的所有派生类中,该函数均保持虚函数的特性。
在派生类中重新定义基类中的虚函数时,可以不用关键字virtual来修饰这个成员函数 。
虚函数是用关键字virtual修饰的某基类中的protected或public成员函数。它可以在派生类中重新定义,以形成不同版本。只有在程序的执行过程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定激活哪一个版本,实现动态聚束。
关于虚函数,说明以下几点:
1、当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同。若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数的重载,而不是虚函数。若函数名不同,显然这是不同的成员函数。
2、实现这种动态的多态性时,必须使用基类类型的指针变量,并使该指针指向不同的派生类对象,并通过调用指针所指向的虚函数才能实现动态的多态性。
class A{
protected: int x;
public: A(){x =1000;}
virtual void print(){ cout<<“x=”<
};
class B:public A{ inty;
public: B() { y=2000;}
void print(){ cout<<“y=”<
};
class C:public A{ intz;
public: C(){z=3000;}
void print(){ cout<<“z=”<
};
void main(void )
{ A a, *pa;
B b; C c;
a.print(); b.print(); c.print(); //静态调用
pa=&a; pa->print();//调用类A的虚函数
pa=&b; pa->print();//调用类B的虚函数
pa=&c; pa->print();}//调用类C的虚函数
3、虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。
4、在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调用这种派生类对象的虚函数时,则调用其基类中的虚函数。
5、可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数。
纯虚函数
在基类中不对虚函数给出有意义的实现,它只是在派生类中有具体的意义。这时基类中的虚函数只是一个入口,具体的目的地由不同的派生类中的对象决定。这个虚函数称为纯虚函数。
class <基类名>
{ virtual <类型><函数名>(<参数表>)=0;
......
};
1、在定义纯虚函数时,不能定义虚函数的实现部分。
2、把函数名赋于0,本质上是将指向函数体的指针值赋为初值0。与定义空函数不一样,空函数的函数体为空,即调用该函数时,不执行任何动作。在没有重新定义这种纯虚函数之前,是不能调用这种函数的。
3、把至少包含一个纯虚函数的类,称为抽象类。这种类只能作为派生类的基类,不能用来说明这种类的对象。
其理由是明显的:因为虚函数没有实现部分,所以不能产生对象。但可以定义指向抽象类的指针,即指向这种基类的指针。当用这种基类指针指向其派生类的对象时,必须在派生类中重载纯虚函数,否则会产生程序的运行错误。
4、在以抽象类作为基类的派生类中必须有纯虚函数的实现部分,即必须有重载纯虚函数的函数体。否则,这样的派生类也是不能产生对象的。
综上所述,可把纯虚函数归结为:抽象类的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并实现动态多态性。
1.作用纯虚函数就是没有函数体的,必须在派生类中重载的函数。虚函数可以有函数体,如果派生类中没有重载函数,则调用父类的函数 class a { virtual b()=0;//纯虚函数 virtual c(){...}//虚函数 }; 2.虚函数主要实现多态机制避免二义性问题至于纯虚函数是抽象类机制,基类提供接口,派生类提供实现抽象类不能定义对象?
3.最大区别:有纯虚函数的类不能定义对象。 基本来说纯虚函数是必须被重载的,因此在被用来做基类的抽象类中肯定有一个或多个纯虚函数。 而虚函数可以在继承类中被重载,也可以不。 二者都是体现了c++的多态性
纯虚函数:
1.表明该纯虚函数所属的类仅做为接口使用,不能实例化(即不能生成一个该类的对象).
2.接口类的子类(derived class)必须overriden每个纯虚函数,使之成为非纯虚函数后,该类才能产充许产生实例;
3.纯虚函数可以有该函数的定义,有可以没有.但纯虚析构函数必须要有定义. 如果一个接口类只有它的析构函数的纯虚的,那么,该接 口类的子类可以不必显式overriden接口类的析构函数,编译器自动产生的析构函数就可以overriden接口类的析构函数,使子类自动成为一个可实例化的普通类.
class Base{ };
class A : public Base{ };
class B: public Base{ };
class C: public A, public B{ };
类C中继承了两个类Base,即有两个类Base的实现部分,在调用时产生了二义性。
因此将Base声明为虚基类,即所有成员函数都是虚函数。
有关静态数据成员的使用,说明以下几点:
1、类的静态数据成员是静态分配存储空间的,而其它成员是动态分配存储空间的(全局变量除外)。当类中没有定义静态数据成员时,在程序执行期间遇到说明类的对象时,才为对象的所有成员依次分配存储空间,这种存储空间的分配是动态的;而当类中定义了静态数据成员时,在编译时,就要为类的静态数据成员分配存储空间。
2、必须在文件作用域中,对静态数据成员作一次且只能作一次定义性说明。
class A{
int i,j;
static int x,y;//定义静态成员
public: A(int a=0,int b=0,int c=0, intd=0){ i=a;j=b;x=c;y=d; }
void Show(){cout << "i="<<i<<'\t'<<"j="<
cout<< "x="<
}
};
int A::x=0; //必须对静态成员作一次定义性说明
int A::y=0;
3、静态数据成员具有全局变量和局部变量的一些特性。静态数据成员与全局变量一样都是静态分配存储空间的,但全局变量在程序中的任何位置都可以访问它,而静态数据成员受到访问权限的约束。必须是public权限时,才可能在类外进行访问。
4、为了保持静态数据成员取值的一致性,通常在构造函数中不给静态数据成员置初值,而是在对静态数据成员的定义性说明时指定初值。
可以将类的成员函数定义为静态的成员函数。即使用关键字static来修饰成员函数 。
对静态成员函数的用法说明以下几点:
1、与静态数据成员一样,在类外的程序代码中,通过类名加上作用域操作符,可直接调用静态成员函数。
2、静态成员函数只能直接使用本类的静态数据成员或静态成员函数,但不能直接使用非静态的数据成员(可以引用使用)。这是因为静态成员函数可被其它程序代码直接调用,所以,它不包含对象地址的this指针。
3、静态成员函数的实现部分在类定义之外定义时,其前面不能加修饰词static。这是由于关键字static不是数据类型的组成部分,因此,在类外定义静态成员函数的实现部分时,不能使用这个关键字
4、不能把静态成员函数定义为虚函数。静态成员函数也是在编译时分配存储空间,所以在程序的执行过程中不能提供多态性。
5、可将静态成员函数定义为内联的(inline),其定义方法与非静态成员函数完全相同。
class Tc {
private:int A;
static intB;//静态数据成员
public:Tc(int a){A=a; B+=a;}
static void display(Tc c);//Tc的对象为形参
};
void Tc::display(Tc c)//类外定义不用static修饰
{ cout<<"A="<<c.A<<",B="<endl; }
int Tc::B=2;
void main(void)
{ Tc a(2),b(4);
Tc::display (a);
Tc::display (b);
}
原文转自:http://blog.sina.com.cn/s/blog_a6b630f50102v5u4.html