友元函数

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

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;
注意:
  1. const是函数类型的一个组成部分,因此在函数的定义部分也要带const关键字。
  2. 常成员函数不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数(这保证了在常成员函数中绝对不会更改数据成员的值)。
  3. 如果将一个对象说明为常对象,则通过该对象只能调用它的常成员函数,而不能调用其他成员函数(这就是C++从语法机制上对常对象的保护,也是常对象唯一的对外接口方式)。
  4. 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++中的友元函数、static函数、常函数小结(理论篇)相关推荐

  1. inline内联函数 static静态函数 普通函数区别

    面试时候一般只会问你区别,所有本文只说区别. 内联函数和普通函数的区别: 内联函数和普通函数最大的区别在于内部的实现方面,当普通函数在被调用时,系统首先跳跃到该函数的入口地址,执行函数体,执行完成后, ...

  2. 如何编写高质量的 JS 函数(3) --函数式编程[理论篇]

    本文首发于 vivo互联网技术 微信公众号  链接:https://mp.weixin.qq.com/s/EWSqZuujHIRyx8Eb2SSidQ 作者:杨昆 [编写高质量函数系列]中, < ...

  3. C++中的常函数和常对象

    函数后加个const,相当于告诉编译器,该函数不会改变成员变量的值 常函数: 返回值 函数名() const{//在普通成员函数后面加上const就是常成员函数} 注意: 只有普通成员函数才能构成常函 ...

  4. C++中的常函数与常对象

    1.常函数 首先上代码: class Person { public:void showPerson() const//函数后面用const修饰了{m_Age = 20;cout << & ...

  5. C++常函数的用法详解

    C++常成员函数用法 1.在一个普通成员函数后面加上const修饰,就是常成员函数 返回值 函数名( 形参表 )const {函数体} 注意: 普通成员函数才有常函数.C++中构造函数,静态成员函数, ...

  6. C++基础知识(常函数和常对象)

    1.常函数  void func()  const {}  常函数 2.常函数  修饰是 this指针  const Type * const this 3.常函数  不能修饰this指针指向的值 4 ...

  7. 常对象与常函数(Const修饰的对象与成员函数)

    常函数: 其定义与注意事项如下:     1.成员函数后加const后,我们称这个函数为常函数     2.常函数内不可修改成员属性     3.成员属性声明时加关键字mutable后,在常函数中依然 ...

  8. 【C++入门】const和mutable关键字、常函数介绍

    1.常函数介绍 1.1.什么是常函数 (1)常函数的定义:类的成员函数对类中任何成员不作修改而只能作访问的函数:简单来说,就是调用类的成员函数不会对类做任何修改: (2)常函数的实现:常函数是依靠co ...

  9. C++ 常函数(const修饰成员函数)

    常函数 const修饰成员函数 const修饰成员函数 常函数: 1.成员函数后加const后我们称这个函数为常函数. 2.常函数不可以修改成员属性. 3.成员属性声明时加关键字mutable后,在常 ...

  10. C++ 常函数 常对象 初步

    1.常函数 如果不想让成员函数修改成员对象, 就将成员函数改为常函数, 成员函数的后面 加 const. 注意:必须在成员函数的声明和定义处同时加上 const 关键字 2. 常对象 比如 const ...

最新文章

  1. 多点优化损失函数地图全局描述
  2. 任正非:华为5G芯片用在iPhone上?我持开放态度
  3. 网页的一般布局(标题和脚注100%,内容宽度固定宽度px)
  4. Spring 事务提交回滚源码解析
  5. Oracle备份还原
  6. android 免root 模拟器,真正免root的Droidwall详细使用教程
  7. 4019-平衡二叉树的高度的计算(C++,附思路)
  8. Prototype使用Hash对象
  9. android动画的实现过程
  10. ubuntu 14.04/14.10 iptables 防火墙设置
  11. Eclipse、STS 常用设置、操作 与 常用快捷键
  12. DevComponents.AdvTree 相关笔记
  13. python抽签代码,python:选房抽签小工具
  14. unity期末大作业消消乐小游戏(附下载链接)
  15. sobel边缘检测java_Sobel边缘检测
  16. python123随机密码生成答案_###随机密码生成###python123
  17. mysql类似于excel的删除重复项_Excel去除重复项的三种常用技巧
  18. HC05蓝牙模块主机与从机进行连接通信
  19. Chrome安装VUE插件
  20. matlab中omg什么意思,英文聊天中omg,jk,lol,Lmao是什么意思

热门文章

  1. CWnd和HWND的区别
  2. (Tekla Structures二次开发)使用VS创建Tekla Structures插件的模板
  3. Allegro Design Entry CIS 和 Orcad Capture CIS 区别
  4. VSSDK 系列 - 创建 灯泡提示 ,替换 单词
  5. mysql错误编号2058_SQLyog连接MySQL时出现的2058错误解决方法
  6. 提高程序可读性的技巧
  7. android 让app全屏显示,Android app设置全屏模式
  8. Ubuntu怎么念?
  9. 【51nod】2591 最终讨伐
  10. UC手游平台九游已成与91手机助手、360并列的国内三大手机游戏平台