第32节 静态成员和友元

第32节 静态成员和友元

1、静态成员

1、静态数据成员
►可以定义类的静态成员,能够实现同类的多个对象之间数据共享。使用类的静态成员的优点是: 
    ►①静态成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突; 
    ►②静态成员可以实施封装,可以是私有成员,而全局对象不可以。 
    ►③静态成员是与特定类关联的,结构清晰。
    
►1. 静态数据成员 ►例:
    class employee { //雇员类定义 
        private: 
        int empNo; 
        int id; 
        char *name; 
        static int count; //静态数据成员 
        …… 
    };

►静态数据成员是类的一种特殊数据成员,它以关键字static开始,声明形式为:
    class 类名 { //类体 static 数据成员类型 数据成员名列表; //静态数据成员声明 };
►例如: 
    class Data { //Data类定义 
    public:
        static int count; //静态数据成员 
        int maxlevel; //非静态公有数据成员 
        Data(int i=0){ …… , count++; } //构造函数 
    private: 
        int level; //非静态私有数据成员 
    };
    int Data::count=0; //静态数据成员定义且初始化
►count设计的目的是计数Data类总共有多少个实例化对象。

在这里插入图片描述

►关于静态数据成员的说明: 
	►(1)通常,非静态数据成员存在于类类型的每个对象中,静态数据成员则独立于该类的任何对
象,在所有对象之外单独开辟空间存储。在为对象所分配的空间中不包括静态数据成员所占的空间。 
	►(2)如果只声明了类而未定义对象,则类的非静态数据成员是不占存储空间的,只有在定义对
象时,才为对象的数据成员分配空间。但是只要在类中定义了静态数据成员,即使不定义任何对象,
也为静态数据成员分配空间,它可以在尚未建立对象时就被引用。
	►(3)访问静态成员时同样需要遵守公有及私有访问规则。
	►(4)静态数据成员必须在类外部定义一次(仅有一次),静态成员不能通过类构造函数进行初
始化,而是在类外定义时进行初始化。定义静态数据成员的方式为:
		数据成员类型 类名::静态数据成员名=初始化式;
	►(5)静态数据成员可用作默认实参,非静态数据成员不能用作默认实参,因为它的值不能独立
于所属的对象而使用。例如:
	class Data { //Data类定义 
        …
        Data& setbkcolor(int=bkcolor); 
        static const int bkcolor = 5; 
    };
	►(6)有了静态数据成员,各对象之间实现了数据共享,因此可以不使用全局变量。
class test 
{ private: 
	int x; 
	int y; 
 public: 
	static int num; 
	static int Getnum() 
	{	x+=5; //错误,静态成员函数不能调用非静态数据成员 
		num+=15; 
		return num; 
	} 
};
int test::num = 10;
int main(void) 
{ 
    test a; 
    cout<<test::num<<endl; 	   //输出10 
    test::num = 20; 
    cout<<test::num<<endl; 	   //输出20 
    cout<<test::Getnum()<<endl;//输出35 
    cout<<a.Getnum()<<endl;    //输出50 
    return 0; 
}
2、静态成员函数
►成员函数也可以定义为静态的,在类中声明函数的前面加static就成了静态成员函数,声明的一般形式为:
    class 类名 { //类体 static 返回类型 函数名(类型1 参数名1,类型2 参数名2,);};
►例如:
    static int getcount() { return count; } //静态成员函数
    
►和静态数据成员一样,静态成员函数是类的一部分,而不是对象的一部分。如果要在类外调用公有的静
态成员函数,可以类作用域运算符(::)和通过对象名调用静态成员函数,例如:
    cout<<Data::getcount()<<'\t'<<d.getcount();
►静态成员函数与非静态成员函数的根本区别是:非静态成员函数有 this指针,而静态成员函数没有
this指针。因此,静态成员函数不能访问本类中的非静态成员。静态成员函数就是专门为了访问静态
数据成员的。
    
►静态成员函数不能被声明为const。
【例32.1】静态成员举例
 1 #include <iostream> 
 2 using namespace std; 
 3 class CTest { 
 4 public: 
 5      CTest() { s_total++; id=s_total; cout<<"构造"<<id<<" ";} 
 6      ~CTest() { s_total--; cout<<"析构"<<id<<" "; } 
 7      static int gettotal() { return s_total; } 
 8 private: 
 9      static int s_total; 
10      int id; 
11 };
12 int CTest::s_total=0; 
13 int main() 
14 { 
15      CTest a,b,c; 
16      CTest *p=new CTest; 
17      cout<<"合计="<<CTest::gettotal()<<" "; 
18      delete p; 
19      cout<<"合计="<<CTest::gettotal()<<" "; 
20      return 0; 
21 }

【例32.2】静态成员举例
class Test 
{
public: 
    void init() { } 
    static void output() { } 
};
int main() 
{ 
    Test::init(); Test::output(); 
    return 0; 
}
► 编译出错,因为不能通过类名来调用类的非静态成员函数init。

【例32.3】静态成员举例 
class Test 
{
public: 
    void init() { } 
    static void output() { } 
};
int main() 
{ 
    Test t; t.init(); t.output(); 
    return 0}
► 编译通过,类的对象可以使用静态成员函数和非静态成员函数。

【例32.4】静态成员举例 
class Test 
{ 
public: 
	void init() { } 
    static void output() { cout<<x<<endl;} 
private: 
    int x; 
};
int main() 
{ 
    Test t; t.output(); 
    return 0; 
}
► 编译出错,因为静态成员函数中不能引用非静态成员。 
► 静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实
例化对象后才有内存空间
    
【例32.5】静态成员举例 
class Test 
{
public: 
    void init() { output();} 
    static void output() { } 
};
int main() 
{ 
    Test t; t.output(); 
    return 0; 
}
► 编译通过,因为类的非静态成员函数可以调用用静态成员函数,但反之不能。

【例32.6】静态成员举例 
class Test 
{
public: 
    Test() { m++;} 
    ~Test() { m--;} 
    static void output(){cout<<m<<endl; } 
private: 
    static int m; 
};
int main() 
{ 
    Test t; t.output(); 
    return 0; 
}
► 链接错误,因为类的静态数据成员在使用前必须先初始化。 
► 如果在main函数前加上int Point :: m = 0; 
► 再编译链接无错误,运行程序将输出1

2、友元函数和友元类

3、友元函数
►C++提供友元(friend)机制,允许一个类将其非公有成员的访问权授予指定的函数或类。友元的声明
只能出现在类定义的内部的任何地方,由于友元不是授予友元关系(friendship)的那个类的成员,所
以它们不受访问控制的影响。通常,将友元声明放在类定义的开始或结尾。 

►友元可以是普通的函数,或已定义的其他类的成员函数,或整个类。将一个函数设为友元,称为友元函
数(friend function),将一个类设为友元,称为友元类(friend class)。友元类的所有成员函
数都可以访问授予友元关系的那个类的非公有成员。 
    
►因此,访问类非公有成员可以有两个用户:类成员和友元。
    
►如果在一个类以外的某个地方定义了一个函数,在类定义中用friend对其进行声明,此函数就称为这
个类的友元函数。友元函数可以访问这个类中的私有成员。
    
【例32.7】友元函数举例 
 1 #include <iostream> 
 2 #include <cmath> 
 3 using namespace std; 
 4 class Point { //Point类 
 5 public:
 6      Point(int _x=0,int _y=0) : x(_x), y(_y) { } 
 7 private: 
 8      int x, y;//私有数据成员 
 9      friend double distance(Point& r1, Point& r2); //友元函数 
10 };
11 double distance(Point& r1, Point& r2) //计算两个点的距离 
12 { 
13      double x = r2.x>r1.x ? r2.x-r1.x : r1.x-r2.x; //访问Point 类私有成员 
14      double y = r2.y>r1.y ? r2.y-r1.y : r1.y-r2.y; //访问Point 类私有成员 
15      return sqrt(x*x+y*y); 
16 } 
17 int main() 
18 { 
19      Point a(1,1),b(5,5); //定义两个点 
20      cout<<distance(a, b); //输出它们的距离 
21      return 0; 
22 }
►友元函数可以是另一个类的成员函数,称为友元成员函数。
    
【例32.7】友元成员函数举例 
 1 class B; //类的前向声明 
 2 class A { //A类 
 3 public: 
 4      A(int _a=0) : a(_a) { } 
 5      void setb(B& r); 
 6 private: 
 7      int a; //私有数据成员 
 8 }; 
 9 class B { //B类 
10 public: 
11      B(int _b=0) : b(_b) { } 
12 private: 
13      int b; //私有数据成员 
14      friend void A::setb(B& r); 
15 };
16 void A::setb(B& r) 
17 { 
18      r.b=a; //访问B的私有成员b 
19 } 
20 int main() 
21 { 
22      return 0; 
23 }
4、友元类
►不仅可以将一个函数声明为友元,还可以将一个类(如B)声明为 另一个类(如A)的友元,这时类B就
是类A的友元类。友元类B中 的所有成员函数都是A类的友元函数,可以访问A类中的所有成员。 
    class 类名 {   //类体
        …
        friend 友类名;
    };
    class 友类名 { //类体};
►关于友元类的说明: 
    ►(1)友元的关系是单向的而不是双向的。如果声明了类B是类A的友元类,不等于类A是类B的友元
类,类A中的成员函数不能访问类B中的私有数据。 
    ►(2)友元的关系不能传递或继承,如果类B是类A的友元类,类C是类B的友元类,不等于类C是类A
的友元类。如果想让类C是类A的友元类,必须显式地在类A中另外声明。

►面向对象程序设计的一个基本原则是封装性和信息隐蔽,而友元却可以访问其他类中的私有成员,突破
了封装原则。友元的使用有助于数据共享,能提高程序的效率,但也不要滥用,要在数据共享与信息隐
蔽之间选择一个恰当的平衡点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值