第八讲:友元

来源:互联网 发布:月相软件 编辑:程序博客网 时间:2024/06/05 08:37

友元


  • 友元是C++提供的一种破坏数据封装和数据隐藏的机制。
    通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。
    可以使用友元函数和友元类。
    为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。

  • 友元函数
    友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问 private 和 protected成员
    作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择。
    访问对象中的成员必须通过对象名。

#include <iostream>#include <cmath>class Point {   //Point类声明public: //外部接口    Point(int x=0, int y=0) : x(x), y(y) { }    int getX() { return x; }    int getY() { return y; }    friend float dist(Point &a, Point &b); private:    //私有数据成员    int x, y;};float dist( Point& a, Point& b) {  double x = a.x - b.x;  double y = a.y - b.y;  return static_cast<float>(sqrt(x * x + y * y));}int main() {  Point p1(1, 1), p2(4, 5);  cout <<"The distance is: ";  cout << dist(p1, p2) << endl;  return 0;}
  • 友元类
    若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。
    声明语法:将友元类名在另一个类中使用friend修饰说明。
class A {  friend class B;public:  void display() {    cout << x << endl;  }private:  int x;}class B {public:  void set(int i);  void display();private:  A a;};void B::set(int i) {   a.x=i;}void B::display() {   a.display();}
  • 友元关系
    友元关系是单向的;声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A类的成员函数却不能访问B类的私有、保护数据。
    友元关系不能传递;
    友元关系不能继承。

  • 共用数据的保护
    C++采用了不少的数据保护措施。最常用的是将数据成员设置成私有数据( private ),以增加数据的安全性和私密性。
    但是,有时候要求数据在能共享的前提下不能被篡改,就需要借助其它手段了。
    什么手段呢?可以采用const,即把要保护的数据定义为常量。

  • 常对象

           在定义对象时指定对象为常对象。常对象中的数据成员为常变量,并且必须要有初值:例如:class Time         // 类定义             { public:                   int hour, minute, sec;                   Time(int h,int m, int s)    //构造函数                      {hour=h;  minute=m; sec=s}                     void  set() { cout<<hour<<minute<<ses; }           };     Time  const  t1(12,34,56);    // 常对象定义注意:在所有的场合中,对象t1的所有数据成员都被保护,不能被修改。因此,凡是希望数据成员不能被改变的对象,可以声明为常对象。其声明格式为:        类名 const  对象名(参数表列);    或者:        const   类名  对象名(参数表列);    两者等价。
    如果一个对象被定义成常对象,那么不能调用该对象的非const 型成员函数,而系统隐含调用的构造函数和析构函数除外。如在上例中:Time  const  t1( 12,34,56);   // 对, 定义t1为常对象t1.set();  // 错误,set 不是const 型,不能调用    为什么会这样?因为成员函数有可能修改数据成员,而成员函数的定义可能和成员函数的声明不在同一文件,系统没法检测。所以,系统只能统一拦截,不让用户调用常对象的成员函数,除非该成员函数被声明成 const 类型。    怎样才能引用常对象的成员函数呢?只需要把该成员函数声明为 const 类型,即常成员函数就行了。方法为:    函数返回类型 函数名(参数表列) const;   例如:void set()  const;    // 将函数声明成const 类型    常成员函数可以访问常对象中的数据成员,但仍然不能修改它们。    有时候编程要求必须修改某个常对象中的数据成员,如某个计数器count ,ANSI C++对此做了特殊的处理,将该数据成员 声明为 mutable,如:mutable int count;此时,常对象的数据成员count,就可以用常成员函数来访问和修改了。
#include <iostream.h>class Time      //类定义{public:     int hour, min, sec;    Time(int=0,int=0,int=0);    void set()                  // 非const成员函数    {cout<<hour<<min<<sec<<endl;}     void  set1( ) const   // const成员函数    {cout<<hour<<" "<<min<<"  "<<sec<<endl;}};Time::Time(int h,int m,int s){hour=h;min=m;sec=s;} int main(){    Time  const  t1(12,34,56);    // 常对象定义         t1.set();  //错误,常对象不能调用该对象的非const 型成员函数。     t1.set1();  return 0;}
  • 常对象成员
1)常数据成员:其作用和用法与一般常变量相似,在类的声明中,用关键词 const 来声明常数据成员,例如:        const  int  hour;注意1 :常数据成员的初始化,不能采用在构造函数中对常数据成员赋初值的方法。如:                          Time::Time(int h)                           {hour=h;}              //非法注意2 :只能通过构造函数的参数初始化表对常数据成员进行初始化,例如:      class Time         // 类定义     { public:          const int h;           Time(int hh):h(hh){}      };        在类外定义构造函数,初始化形式为:        Time::Time ( int h ): hour ( h ) { }        在类中声明了某一个数据成员为常数据成员后,该类的所有对象中的该数据成员的值是不可改变的(不输入、不赋值),但可以是不同的(由每个对象的参数初始化表决定)。

2) 常成员函数
一般的成员函数可以引用本对象中的非const 数据成员,也可以修改它们。但如果成员函数声明为常成员函数,则只能引用本对象中的数据成员,而不能修改它们。如:
void get_Time( ) const; // const位置在最后
const 是函数类型的一部分,在声明函数和定义函数时都要用到 const 关键字。
常成员函数可以引用常数据成员,也可以引用非const数据成员;而常数据成员可以被常成员函数引用,也可以被非 const 成员函数引用。

|常对象成员

数据成员 非const成员函数 const 成员函数 非const 数据成员 可引用,可修改 可引用,不可修改 const 数据成员 可引用,不可修改 可引用,不可修改 常对象的数据成员 不可引用,不可修改 可引用,不可修改

怎样使用?
1)类中如果一些数据成员需要保护,另一些数据成员不需保护,将需要保护的数据成员声明为 const ,以保证其值不被改变。
2)类中如果所有数据成员都需保护,可以将所有数据成员都声明成 const ,本对象的任何成员函数,都只能引用,不能改变它们。或者将这些数据成员所属的对象声明成const,只让本对象的const 成员函数可引用,但不能改变。
3)如果一个对象被定义成了常对象,只能调用其中的const成员函数,不能调用非const成员函数。
4)常成员函数不能调用另一个非const成员函数

#include <iostream.h>  // 常成员函数举例1class Time{public:     int hour, min, sec;    Time(int=0,int=0,int=0);  // 构造函数    void  set1( ) const   // const成员函数    {  cin>>hour>>min>>sec;                 // 错误,常成员函数不允许修改常对象中数据成员的值        cout<<hour<<" "<<min<<"  "<<sec<<endl;                 // 常成员函数可以访问常对象中的数据成员       }};Time::Time(int h,int m,int s){hour=h;min=m;sec=s;} void main(){  Time  const  t1(12,34,56);    // 常对象定义     t1.set1();}       如果需要访问常对象中的成员函数,则需要将其声明为const,表示这是一个常成员函数。 #include <iostream.h>    // 常成员函数举例2class Time{public:     mutable int hour, min, sec;    Time(int=0,int=0,int=0);    void  set1( ) const   // const成员函数    { cin>>hour>>min>>sec;  // mutable 数据成员,可以改变      cout<<hour<<" "<<min<<"  "<<sec<<endl;}};Time::Time(int h,int m,int s){hour=h;min=m;sec=s;} int main(){    Time  const  t1(12,34,56);    // 常对象定义         t1.set1();         return 0;}要修改常对象中某个数据成员的值,则可将该数据成员声明为mutable。举例:
  • 常引用作形参
#include <iostream>#include <cmath>using namespace std;class Point {   //Point类定义public: //外部接口    Point(int x = 0, int y = 0)    : x(x), y(y) { }    int getX() { return x; }    int getY() { return y; }    friend float dist(const Point &p1, const Point &p2);private:    //私有数据成员    int x, y;};float dist(const Point &p1, const Point &p2) {    double x = p1.x - p2.x;     double y = p1.y - p2.y;    return static_cast<float>(sqrt(x * x + y * y));}int main() {    //主函数    const Point myp1(1, 1), myp2(4, 5);     cout << "The distance is: ";    cout << dist(myp1, myp2) << endl;    return 0;} 
  • 常类型
    常类型的对象必须进行初始化,而且不能被更新。
    常对象:必须进行初始化,不能被更新。
    const 类名 对象名
    常引用:被引用的对象不能被更新。
    const 类型说明符 &引用名
    常数组:数组元素不能被更新(下一章介绍)。
    类型说明符 const 数组名[大小]…
    常指针:指向常量的指针(下一章介绍)。
class A{  public:    A(int i,int j) {x=i; y=j;}                     ...  private:    int x,y;};A const a(3,4); //a是常对象,不能被更新
  • 用const修饰的对象成员
    常成员函数
    使用const关键字说明的函数。
    常成员函数不更新对象的数据成员。
    常成员函数说明格式: 类型说明符 函数名(参数表)const; 这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字。
    const关键字可以被用于参与对重载函数的区分
    通过常对象只能调用它的常成员函数。
    常数据成员
    使用const说明的数据成员。
#include<iostream>using namespace std;class R {public:  R(int r1, int r2) : r1(r1), r2(r2) { }  void print();  void print() const;private:  int r1, r2;};void R::print() {  cout << r1 << ":" << r2 << endl;}void R::print() const {  cout << r1 << ";" << r2 << endl;}int main() {  R a(5,4);  a.print(); //调用void print()  const R b(20,52);    b.print(); //调用void print() const    return 0;}
#include <iostream>using namespace std;class A {public:    A(int i);    void print();private:    const int a;    static const int b;  //静态常数据成员};const int A::b=10; A::A(int i) : a(i) { }void A::print() {  cout << a << ":" << b <<endl;}int main() {/*建立对象a和b,并以100和0作为初值,分别调用构造函数,通过构造函数的初始化列表给对象的常数据成员赋初值*/  A a1(100), a2(0);  a1.print();  a2.print();  return 0;}

常引用作形参

#include <iostream>#include <cmath>using namespace std;class Point {   //Point类定义public: //外部接口    Point(int x = 0, int y = 0)    : x(x), y(y) { }    int getX() { return x; }    int getY() { return y; }    friend float dist(const Point &p1, const Point &p2);private:    //私有数据成员    int x, y;};float dist(const Point &p1, const Point &p2) {    double x = p1.x - p2.x;     double y = p1.y - p2.y;    return static_cast<float>(sqrt(x * x + y * y));}int main() {    //主函数    const Point myp1(1, 1), myp2(4, 5);     cout << "The distance is: ";    cout << dist(myp1, myp2) << endl;    return 0;}