C++学习
三、静态成员与友元
1、常量(C++和C语言一样使用const)
class A{
public:
A():x(100){}
const int x;//常数据成员
void func() const;//常成员函数
const A a;//常对象
}
例9、常成员函数以及构造函数传参特点
#include <stdio.h>
class A{
public:
A(int data = 0,int a = 1):b(data){//初始化表写法
//一般情况所传参数data赋值给b是在{}里写b=data; 但这里b是常量(const int b)所以只能写成:b(data),为了方便于是就统一写成:b(data)这种初始化表写法。
this->a = a;//this修饰的a是下面的私有成员int a,使用this是为区分开传参的a
printf("AAAAAAAAA\n");
}
~A(){
printf("~~~~~~~~~\n");
}
void show(void) const
{
printf("a = %d\n", a);
printf("b = %d\n", b);
}
private:
int a;
const int b;
};
int main()
{
A x(6);
x.show();
A y(7);
y.show();
A z;
z.show();
}
结果:
@ubuntu:/mnt/hgfs/ub2/class$ g++ const.cpp
@ubuntu:/mnt/hgfs/ub2/class$ ./a.out
AAAAAAAAA
a = 1
b = 6
AAAAAAAAA
a = 1
b = 7
AAAAAAAAA
a = 1
b = 0
~~~~~~~~~
~~~~~~~~~
~~~~~~~~~
@ubuntu:/mnt/hgfs/ub2/class$
根据结果情况发现,a值一直是默认值1,这是因为A x(6);的6是传给data,然后再赋值给b,最后打印出b。构造函数传参是有先后的,data在前所以参数赋给data。
2、静态成员(属于类不属于对象)
static int x;
static const int x = 10;
static int A::x = 10;//类外
static void func();
A::func();//类外调用
例10、无对象情况访问类成员
#include <stdio.h>
class A{
public:
void func(void)
{
printf("xxxxxxxxx\n");
}
};
int main()
{
A a;
a.func();
}
上边展示的是一般情况的函数使用:先创建个对象a,然后再由a去访问函数func()。
然而有时候想不创建对象就访问类成员函数,于是可以写成:
A::func();
于是有:
#include <stdio.h>
class A{
public:
void func(void)
{
printf("xxxxxxxxx\n");
}
};
int main()
{
A a;
a.func();
A::func();
}
结果报错:
@ubuntu:/mnt/hgfs/ub2/class$ g++ static_1.cpp
static_1.cpp: In function ‘int main()’:
static_1.cpp:15:10: error: cannot call member function ‘void A::func()’ without object
A::func();
^
@ubuntu:/mnt/hgfs/ub2/class$
错误是因为普通成员函数是需要先创建对象然后根据对象去进行类成员函数访问,能无对象情况访问的是静态成员函数。总的来讲,普通成员函数拒绝单身狗(所以静态就是香)。
改成静态成员函数后如下:
#include <stdio.h>
class A{
public:
static void func(void)
{
printf("xxxxxxxxx\n");
}
};
int main()
{
A a;
a.func();
A::func();
}
结果:
@ubuntu:/mnt/hgfs/ub2/class$ g++ static_1.cpp
@ubuntu:/mnt/hgfs/ub2/class$./a.out
xxxxxxxxx
xxxxxxxxx
@ubuntu:/mnt/hgfs/ub2/class$
对于成员函数是这样,对于其它数据类型成员亦是如此,比如static int data:
#include <stdio.h>
class A{
public:
static void func(void)
{
printf("xxxxxxxxx\n");
}
static int data;
};
int A::data = 0;//静态成员需要初始化(在类外)
int main()
{
A a;
a.func();
A::func();
//普通情况
A x;
x.data = 6;
printf("x.data = %d\n", x.data);
//无对象情况
A::data = 7;
printf("A::data = %d\n", x.data);
}
结果:
@ubuntu:/mnt/hgfs/ub2/class$ g++ static_2.cpp
@ubuntu:/mnt/hgfs/ub2/class$./a.out
xxxxxxxxx
xxxxxxxxx
x.data = 6
A::data = 7
@ubuntu:/mnt/hgfs/ub2/class$
3、友元(破坏封装)
一开始,我们是为了安全性而捆绑封装在一起,但后来发现有些情况下还是要对封装里的成员进行针对性的操作改动,于是就需要友元,让本来私有的可以特定的情况下使用。
friend class B;//友元类
friend void func();//友元函数
friend void B::func();//友元成员函数
在讲友元之前,先回顾下之前的一个案例:
例11-1、数组末尾追加数据(回顾)
arr.h
#ifndef _ARR_
#define _ARR_
class ARR{
public:
ARR():tail(0){
}
void addtail(int data);
void show(void);
private:
int data[100];
int tail;
};
#endif
arr.cpp
#include "arr.h"
#include <stdio.h>
void ARR::addtail(int data)
{
this->data[tail++] = data;
}
void ARR::show(void)
{
int i = 0;
for(;i<tail; i++)
printf("%d, ", data[i]);
printf("\n");
}
main.cpp
#include "arr.h"
int main()
{
ARR arr;
arr.addtail(1);
arr.addtail(2);
arr.addtail(5);
arr.addtail(8);
arr.addtail(0);
arr.show();
}
结果:
@ubuntu:/mnt/hgfs/ub2/ARR4$ ls
arr.cpp arr.h main.cpp
@ubuntu:/mnt/hgfs/ub2/ARR4$ g++ *.cpp
@ubuntu:/mnt/hgfs/ub2/ARR4$ ./a.out
1, 2, 5, 8, 0,
@ubuntu:/mnt/hgfs/ub2/ARR4$
现在想对以上的数据进行倒序(变成08521)。于是尝试如下:
对main.c修改成
#include "arr.h"
void rev(ARR &arr)
{
int i = 0;
for(;i<arr.tail/2;i++)
{
int tem = arr.data[i];
arr.data[i] = arr.data[tail-i-1];
arr.data[arr.tail-i-1] = tem;
}
}
int main()
{
ARR arr;
arr.addtail(1);
arr.addtail(2);
arr.addtail(5);
arr.addtail(8);
arr.addtail(0);
arr.show();
rev(arr);
arr.show();
}
结果报错:
@ubuntu:/mnt/hgfs/ub2/ARR4$ ls
arr.cpp arr.h main.cpp
@ubuntu:/mnt/hgfs/ub2/ARR4$ g++ *.cpp
In file included from main.cpp:1:0:
arr.h: In function ‘void rev(ARR&)’:
arr.h:15:6: error: ‘int ARR::tail’ is private
int tail;
^
main.cpp:6:16: error: within this context
for(;i<arr.tail/2;i++)
^
In file included from main.cpp:1:0:
arr.h:14:14: error: ‘int ARR::data [100]’ is private
int data[100];
^
main.cpp:8:23: error: within this context
int tem = arr.data[i];
^
In file included from main.cpp:1:0:
arr.h:14:14: error: ‘int ARR::data [100]’ is private
int data[100];
^
main.cpp:9:13: error: within this context
arr.data[i] = arr.data[arr.tail-i-1];
^
In file included from main.cpp:1:0:
arr.h:14:14: error: ‘int ARR::data [100]’ is private
int data[100];
^
main.cpp:9:27: error: within this context
arr.data[i] = arr.data[arr.tail-i-1];
^
In file included from main.cpp:1:0:
arr.h:15:6: error: ‘int ARR::tail’ is private
int tail;
^
main.cpp:9:36: error: within this context
arr.data[i] = arr.data[arr.tail-i-1];
^
In file included from main.cpp:1:0:
arr.h:14:14: error: ‘int ARR::data [100]’ is private
int data[100];
^
main.cpp:10:13: error: within this context
arr.data[arr.tail-i-1] = tem;
^
In file included from main.cpp:1:0:
arr.h:15:6: error: ‘int ARR::tail’ is private
int tail;
^
main.cpp:10:22: error: within this context
arr.data[arr.tail-i-1] = tem;
^
@ubuntu:/mnt/hgfs/ub2/ARR4$
错误信息里有:
arr.h:15:6: error: ‘int ARR::tail’ is private
arr.h:14:14: error: ‘int ARR::data [100]’ is private
说明越界去访问私有成员时被拒绝了。如果不想这种情况的访问被拒绝,就需要在类里边把函数申请为友元。(当然,也可以把这个函数归到类里边作为静态成员函数然后通过类去调用)
例11-2、倒序(变成从后往前,不是排序)
arr.h
#ifndef _ARR_
#define _ARR_
class ARR{
public:
ARR():tail(0){
}
void addtail(int data);
void show(void);
friend void rev(ARR &arr);//把rev函数标记为友元
private:
int data[100];
int tail;
};
#endif
arr.cpp
#include "arr.h"
#include <stdio.h>
void ARR::addtail(int data)
{
this->data[tail++] = data;
}
void ARR::show(void)
{
int i = 0;
for(;i<tail; i++)
printf("%d, ", data[i]);
printf("\n");
}
main.cpp
#include "arr.h"
void rev(ARR &arr)
{
int i = 0;
for(;i<arr.tail/2;i++)
{
int tem = arr.data[i];
arr.data[i] = arr.data[arr.tail-i-1];
arr.data[arr.tail-i-1] = tem;
}
}
int main()
{
ARR arr;
arr.addtail(1);
arr.addtail(2);
arr.addtail(5);
arr.addtail(8);
arr.addtail(0);
arr.show();
rev(arr);
arr.show();
}
结果:
@ubuntu:/mnt/hgfs/ub2/ARR5$ ls
arr.cpp arr.h main.cpp
@ubuntu:/mnt/hgfs/ub2/ARR5$ g++ *.cpp
@ubuntu:/mnt/hgfs/ub2/ARR5$ ./a.out
1, 2, 5, 8, 0,
0, 8, 5, 2, 1,
@ubuntu:/mnt/hgfs/ub2/ARR5$
关键字friend标记后就可以进行私有成员访问了。
例11-3、友元类成员(类B的成员成为类A的友元然后类B的成员就可以访问A的私有成员)
首先类A创建一个对象x去访问私有成员y(A x; x.y),然后在类B里面创建个成员函数printfA()打印类A的对象x的私有成员y。于是,类B就可以创建个对象b,然后通过其成员函数去实现类A私有成员y的打印(b.printfA(a);)。 一般情况下只是其某成员比如void printfA(A &x) 需要友元化,其它的类B的成员不需要,所以把整个类B申请为友元不合适。
#include <stdio.h>
class A;//这里的存在要引起注意,因为我刻意把案例变得类A中有类B友元处理,类B中成员函数用到类A。
//这种你中有我我中有你就需要这样的重复标识。
class B{
public:
void printfA(A &x);
};
class A{
public:
A()
{
y = 100;
}
// friend class B;//这句是直接一刀切把整个类友元化
friend void B::printfA(A &x);//只是针对类B中的一个成员函数友元化
private:
int y;
};
void B::printfA(A &x)
{
printf("%d\n", x.y);
}
int main()
{
A a;
// printf("%d\n", a.x);
B b;
b.printfA(a);
}
结果:
@ubuntu:/mnt/hgfs/ub2$ g++ friend.cpp
@ubuntu:/mnt/hgfs/ub2$ ./a.out
100
@ubuntu:/mnt/hgfs/ub2$