面向对象¶
C++是面向对象的语言,面向对象最重要的功能就是封装,封装就要用到访问函数,封装数据。
面向对象的设计:数据做成私有的,封装。
例如链表设计:
- 把Link(链表类)做成Node(节点类)的友元类
前置声明。节点中有一个链表的友元,链表可以访问节点的东西。
- 做成嵌套类
把节点类做成链表类私有的成员类。
一个类在另一个类的内部,外面的类就可以操作里面的类的私有数据成员。
嵌套类里面的类的数据成员是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,并且数据成员第一个字母改成大写。