跳转至

面向对象

C++是面向对象的语言,面向对象最重要的功能就是封装,封装就要用到访问函数,封装数据。

面向对象的设计:数据做成私有的,封装。

例如链表设计:

  1. 把Link(链表类)做成Node(节点类)的友元类

前置声明。节点中有一个链表的友元,链表可以访问节点的东西。

  1. 做成嵌套类

把节点类做成链表类私有的成员类。

一个类在另一个类的内部,外面的类就可以操作里面的类的私有数据成员。

嵌套类里面的类的数据成员是public,不违反面向对象的风格。

嵌套可能会使结构或类变得很大。

很多结构的时候,使用友元friend。

友元friend

访问私有函数或变量,需要使用友元。

全局函数、成员函数、结构/类,都可以作为友元。

类(结构)的成员函数可以类(结构)使用私有变量

前置声明可以省略不写。

作为参数也可以访问私有变量

不是友元不能对私有的数据进行操作。

可以把整个结构设置为友元

struct X; //声明

struct Y {
    void f();
    void e(X*);
};

//void h();//声明
struct X {
private:
    int i; //私有
public:
    void initialize();//共有
    friend void Y::f(); //友元
    friend void Y::e(X *); //友元
//    friend void Z::m(X *);
    friend struct Z;//如果Z里很多函数都需要访问X的私有成员,把整个结构Z设置为友元
    friend void g(X*, int); //友元
    friend void h(); //友元
};

void X::initialize() {
    i = 0;
}

struct Z {
private:
    int i;
public:
    void initialize();
    void m(X* x);
};
void Z::m(X *x) {
    x->i = 11;
    cout << x->i << endl;
}

void Y::f() {
    X x;
    x.i = 99;
    cout << x.i << endl;
}
void Y::e(X *x) {
    x->i = 88;
    cout << x->i << endl;
}

//也可以用参数的形式传递,声明为友元就可以访问i
void g(X* x, int i) {
    x->i = i;
}

//h想要使用X的i 必须是X的友元
void h() {
    cout << "hello h" << endl;
    X x;
    x.i = 12;
    g(&x, 100);
    cout << x.i << endl;
}

void test() {
    h();
}

两个结构

使用Pointer操作Holder的a数组。

const int sz = 20;//常量数组大小
struct Pointer;
struct Holder {
private:
    int a[sz];//私有数组
public:
    void initialize();
    friend struct Pointer;
};

struct Pointer {
private:
    Holder* h;
    int* p;
public:
    void initialize(Holder* h);
    void next();
    void previous();
    void top();
    void end();
    int read();//读取
    void set(int i);
};

void Holder::initialize() {
    memset(a, 0, sz*sizeof(int));
}
void Pointer::initialize(Holder *rv) {
    h = rv;
    p = rv->a;//指针指向数组a
}
//指向下一个
void Pointer::next() {
    if (p < &h->a[sz - 1]) {
        p++;
    }
}
//前一个
void Pointer::previous() {
    if (p > &h->a[0]) {
        p--;
    }
}
//第一个
void Pointer::top() {
    p = &(h->a[0]);
}
//最后一个
void Pointer::end() {
    p = &(h->a[sz-1]);
}
int Pointer::read() {
    return *p;
}
//修改指针指向的数
void Pointer::set(int i) {
    *p = i;
}

调用

void test() {
    Holder h;
    Pointer hp;

    h.initialize();
    hp.initialize(&h);

    for (int i = 0; i<sz; i++) {
        hp.set(i);
        hp.next();
    }

    //把指针调整到第一个
    for (int i = 0; i<sz; i++) {
        cout << "hp = " << hp.read() << endl;
        hp.next();
     }
}

访问函数

把数据成员做成public就破坏了封装。所以封装的时候成员函数做成私有的,不能直接对数据操作,通过成员函数对类的数据进行操作和使用。

这些函数就叫访问函数。通过访问函数对数据进行访问和修改。

访问器和修改器都比较短小,所以做成内联函数比较好。速度更快。

可以为每一个数据成员写一个访问器,修改器。

数据成员前面加一个下划线_,访问器和修改器都可以写不加下划线的同一个名字。

C和C++保留了这种写法,所以自己最好不要用下划线的这种方法。

几种方法:

  • 用不同的单词,成员变量和访问器修改器名字不一样。(名字不容易写,词汇量有要求)

  • 数据成员前面加下划线 (被C和C++保留了,自己最好不要用)

  • 数据成员前面加两个下划线__ (不常见)
  • 数据成员前面加m_
  • 不修改数据成员,修改访问器和修改器的名字,前面加get和set,并且数据成员第一个字母改成大写。