您的位置:首页 > 编程语言 > C语言/C++

C++中的友元函数、static函数、常函数小结(理论篇)

2016-07-24 01:04 309 查看


                                               友元函数

一个常规的成员函数声明描述了三件在逻辑上相互不同的事情

1.  该函数能访问类声明的私用部分

2.  该函数位于类的作用域之中

3.  该函数必须经由一个对象去激活(有一个this指针)

  

通过将函数声明为友元函数

1.  则 2,3条件失效,

2.  友元函数不是类的成员函数

    3. 友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。

    4. 某类的友元函数的作用域并非该类作用域。如果该友元函数是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同

为什么要使用友元函数
在实现类之间数据共享时,减少系统开销,提高效率。如果类A中的函数要访问类B中的成员(例如:智能指针类的实现),那么类A中该函数要是类B的友元函数。具体来说:为了
使其他类的成员函数直接访问该类的私有变量。即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数。
实际上具体大概有下面两种情况需要使用友元函数:
(1)运算符重载的某些场合需要使用友元
(2)两个类要共享数据的时候。
使用友元函数的优缺点
优点:能够提高效率,表达简单、清晰。
缺点:友元函数破环了封装机制,尽量不使用成员函数,除非不得已的情况下才使用友元函数。

友元函数的使用
友元函数的参数:
因为友元函数没有this指针,则参数要有三种情况:
 1.要访问非static成员时,需要对象做参数;
2. 要访问static成员或全局变量时,则不需要对象做参数;
如果做参数的对象是全局对象,则不需要对象做参数
友元函数的位置
因为友元函数是类外的函数,所以它的声明可以放在类的私有段或公有段且没有区别。
友元函数的调用
可以直接调用友元函数,不需要通过对象或指针
友元函数的分类:
根据这个函数的来源不同,可以分为三种方法:
1.普通函数友元函数
 目的:使普通函数能够访问类的友元
语法:
声明: friend + 普通函数声明
实现位置:可以在类外或类中
实现代码:与普通函数相同
调用:类似普通函数,直接调用
2.4.1.3代码:

class INTEGER
 {
  friend void Print(const INTEGER& obj);//声明友元函数
 };
void Print(const INTEGER& obj)
{
   //函数体
}
void main()
{
  INTEGER obj;
  Print(obj);//直接调用
}

类Y的所有成员函数都为类X友元函数—友元类
目的:使用单个声明使Y类的所有函数成为类X的友元,它提供一种类之间合作的一种方式,使类Y的对象可以具有类X和类Y的功能。
语法:
声明位置:公有私有均可,常写为私有(把类看成一个变量)
声明: friend + 类名(不是对象哦)
2.4.2.3代码:

class girl;
class boy
{
public:
  void disp(girl &);
};
void boy::disp(girl &x) //函数disp()为类boy的成员函数,也是类girl的友元函数
{
  cout<<"girl's name is:"<<x.name<<",age:"<<x.age<<endl;//借助友元,在boy的成员函数disp中,借助girl的对象,直接访问girl的私有变量
}
class girl
{
private:
  char *name;
  int age;
  friend boy; //声明类boy是类girl的友元
};

main函数就不写了和普通调用时一样的。
 
3.类Y的一个成员函数为类X的友元函数
目的:使类Y的一个成员函数成为类X的友元,具体而言:在类Y的这个成员函数中,借助参数X,可以直接以X的私有变量
语法:
声明位置:声明在公有中 (本身为函数)
声明:friend + 成员函数的声明
调用:先定义Y的对象y---使用y调用自己的成员函数---自己的成员函数中使用了友元机制

小结:其实一些操作符的重载实现也是要在类外实现的,那么通常这样的话,声明为类的友元是必须滴。
 
友元函数和类的成员函数的区别
1.成员函数有this指针,而友元函数没有this指针。
2.友元函数是不能被继承的,就像父亲的朋友未必是儿子

                                              静态函数

通过将函数声明为static,可以让它只有前两种性质

1.static 是封闭了this指针,所以他只能访问static成员数据

2.普通方法可以调用 static方法,static方法不可调动普通方法,静态可以调用静态方法

3.普通方法可以访问static data

一. 定义

若类的方法前加了static关键字,则该方法称为静态方法,反之为非静态方法(也称实例方法)。

二. 特点

静态方法为类所有,可以通过对象来使用,也可以通过类来使用。但一般提倡通过类名来使用,因为静态方法只要定义了类,不必建立类的实例就可使用。静态方法只能用类的静态成员。

三. 方法使用

静态方法与静态变量一样,属于类本身,而不属于那个类的一个对象。调用一个被定义为static的方法,可以通过在它前面加上这个类的名称,也可以像调用非静态方法一样通过类对象调用。 

实例方法必须通过类的实例来使用。实例方法可以使用类的非静态成员,也可以使用类的静态成员。 

类的静态方法、静态变量是在类装载的时候装载的。但是要特别注意,类的静态变量是该类的对象所共有的,即是所有对象共享变量。所以建议尽量少用静态变量。尽量在静态方法中使用内部变量。

1..方法声明 

声明静态方法的语法如下: 

<访问修饰符>static返回类型 方法名(参数列表) 

{//方法体} 

2. 方法调用 

静态方法与实例方法唯一不同的,就是静态方法在返回类型前加static关键字。静态方法的调用有两种途径: 

(1)通过类的实例对象去调用 

调用格式为: 对象名.方法名 

(2) 通过类名直接调用 

调用格式为: 类名::方法名 

3. 方法规则 

静态方法只能访问类的静态成员,不能访问类的非静态成员; 

非静态方法可以访问类的静态成员,也可以访问类的非静态成员; 

静态方法既可以用实例来调用,也可以用类名来调用。

四. 实际应用

1.有静态属性的类,一般会定义静态方法。 

2. 没有属性的类,一般会定义静态方法,这样在使用时,通过类名::方法名即可调用,而不用先定义对象再调用,这样可以省去代码。

五. 经典案例

1.用于对静态字段、只读字段等的初始化。 

2.添加static关键字,不能添加访问修饰符,因为静态构造函数都是私有的。 

3.类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用类的任何静态成员才激发静态构造函数 

4.静态构造函数是不可继承的,而且不能被直接调用。 

5.如果类中包含用来开始执行的 main 方法,则该类的静态构造函数将在调用 main方法之前执行。任何带有初始值设定项的静态字段,则在执行该类的静态构造函数时,先要按照文本顺序执行那些初始值设定项。 

6.如果没有编写静态构造函数,而这时类中包含带有初始值设定的静态字段,那么编译器会自动生成默认的静态构造函数。

C++类中静态变量和静态方法使用介绍

静态成员的提出是为了解决数据共享的问题。实现共享有许多方法,如:设置全局性的变量或对象是一种方法。但是,全局变量或对象是有局限性的。这一章里,我们主要讲述类的静态成员来实现数据的共享。
  静态数据成员
  在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。
  使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
  静态数据成员的使用方法和注意事项如下:
  1、静态数据成员在定义或说明时前面加关键字static。//静态变量的定义
  2、静态成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式如下:
    <数据类型><类名>::<静态数据成员名>=<值>  //静态变量的初始化
  这表明:
         (1) 初始化在类体外进行,而前面不加static,(这点需要注意)以免与一般静态变量或对象相混淆。
  (2) 初始化时不加该成员的访问权限控制符private,public等。
  (3) 初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。
  3、静态数据成员是静态存储的,它是静态生存期,必须对它进行初始化。
  4、引用静态数据成员时,采用如下格式:
   <类名>::<静态成员名>   //静态变量的使用方式
  如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。
  下面举一例子,说明静态数据成员的应用:

1 class StaticTest
2 {
3 public:
4     StaticTest(int a, int b, int c);
5     void GetNumber();
6     void GetSum();
7     static void f1(StaticTest &s);
8 private:
9     int A, B, C;
10     static int Sum;
11 };
12
13
14
15 #include "StaticTest.h"
16 #include <iostream>
17 using namespace std;
18
19 int StaticTest::Sum = 0;//静态成员在此初始化
20
21 StaticTest::StaticTest(int a, int b, int c)
22 {
23     A = a;
24     B = b;
25     C = c;
26     Sum += A + B + C;
27 }
28
29 void StaticTest::GetNumber()
30 {
31     cout << "Number = " << endl;
32 }
33
34 void StaticTest::GetSum()
35 {
36     cout << "Sum = " << Sum <<endl;
37 }
38
39 void StaticTest::f1(StaticTest &s)
40 {
41
42     cout << s.A << endl;//静态方法不能直接调用一般成员,可以通过对象引用实现调用
43     cout << Sum <<endl;
44 }
45
46 #include "StaticTest.h"
47 #include <stdlib.h>
48
49
50 int main(void)
51 {
52     StaticTest M(3, 7, 10), N(14, 9, 11);
53     M.GetNumber();
54     N.GetSum();
55     M.GetNumber();
56     N.GetSum();
57     StaticTest::f1(M);
58     system("pause");
59     return 0;
60 }


 
注意,static成员的初始化要在实现中进行,不能在头文件进行。
从输出结果可以看到Sum的值对M对象和对N对象都是相等的。这是因为在初始化M对象时,将M对象的三个int型数据成员的值求和后赋给了Sum,于是Sum保存了该值。在初始化N对象时,对将N对象的三个int型数据成员的值求和后又加到Sum已有的值上,于是Sum将保存另后的值。所以,不论是通过对象M还是通过对象N来引用的值都是一样的,即为54,s.A=3。
静态成员函数
  静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
  在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);
 

                                                常方法

使用const关键字修饰的函数为常成员函数,声明格式如下:
类型说明符  函数名(参数表) const;
注意:

const是函数类型的一个组成部分,因此在函数的定义部分也要带const关键字。

常成员函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数(这保证了在常成员函数中绝对不会更改数据成员的值)。

如果将一个对象说明为常对象,则通过该对象只能调用它的常成员函数,而不能调用其他成员函数(这就是C++从语法机制上对常对象的保护,也是常对象唯一的对外接口方式)。

const关键字可以用于对重载函数的区分,例如,如果在类中这样声明:

void print();

void print() const;

这是对print的有效重载

常对象

常对象必须在定义对象时就指定对象为常对象。

常对象中的数据成员为常变量且必须要有初始值,如

复制代码代码如下:

Time const t1(12,34,36); //定义t1为常对象

这样的话,在所有的场合中,对象t1中的所有数据成员的值都不能被修改。凡希望保证数据成员不被改变的对象,可以声明为常对象。

定义常对象的一般形式为

类名    const    对象名(实参列表);

也可以把const写在最左面

const    类名    对象名(实参列表);

二者等价

如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数(除了由系统自动调用的隐式构造函数和析构函数)。

样做是为了方式非const型的成员函数修改常对象中的数据成员的值,因为const型的成员函数是不可以修改对象中的数据成员的值的(这个后面还会说到)。

那么,怎样才能引用常变量中的数据成员呢?很简单,我们只需要将该成员函数声明称const型的成员函数(常成员函数)即可。

复制代码代码如下:

void Print() const;

常成员函数可以访问常对象中的数据成员,但仍然不允许修改常对象中数据成员的值。

有时在编程时有要求,一定要修改常对象成员中的某个数据成员的值(例如类中有一个用于计数的变量count,其值应当不能变化),

对该数据成员声明为mutable,如

复制代码代码如下:

mutable int count;//定义一个在常对象中可以被改变的数据成员

把count

1.常数据成员
其作用和用法与一般常变量相似,用关键字const来声明常数据成员。常数据成员的值是不能改变的。

只能通过构造函数的参数初始化表对常数据成员进行初始化。

在类体中声明常数据成员

复制代码代码如下:

const int num;//声明hour为常数据成员

在类外定义构造函数

复制代码代码如下:

Student::Student(int n,float s):num(n),score(s){} //通过参数初始化表对常数据成员num和score初始化

在类体中声明了某一个数据成员为常数据成员后,该类所有对象中的该数据成员的值都是不能改变的,但不同对象中该变量成员的值可以是不同的(分别在初始化时指定)。

2.常成员函数

如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们。

注意:常对象只能引用常成员函数

常成员函数的定义形式:

复制代码代码如下:

void Print() const;//注意const的位置在函数名和括号之后

const是函数类型的一部分,在声明函数和定义函数都要有const关键字,在调用时不必加const。

常成员函数可以const数据成员,也可以引用非const数据成员,但是都不能修改他们;

不是常成员函数的函数可以调用const数据成员,但是不能修改它们,也可以调用非const数据成员,并且可以修改它们。

具体情况,如图1:



还要注意一下三点:
1.不要误认为常对象中的成员函数都是常成员函数,常对象只保证其所有数据成员的值不被修改。

2.如果在常对象中的成员函数未加const声明,则编译系统会把它当最非const成员函数处理。

3.还要注意,常成员函数不能调用另外一个非cosnt成员函数。

声明为可变的数据成员,这样就可以用声明为const的成员函数来修改它的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 常方法 静态方法