C++编程中经常出现两个不同的类对象之间相互访问数据的需要,但是成员变量不是public形式,无法直接访问相应的变量,抛开全局变量不谈,常用的方法有:1类封装留下的接口函数、2友元机制、3类静态成员变量。
回顾:
类是面向对象程序设计语言中的一个概念。类是对某个对象定义,它含有有关对象的动作方式的信息,包括他的名称,方法,属性和事件
1 | 公有(public) | 成员可以在类外访问 | |
---|---|---|---|
2 | 私有(private) | 成员只能在累的成员函数之间进行访问 | |
3 | 保护(protected) | 只能在类的成员以及派生类的成员函数之间 | |
简单理解封装和接口接口,简单说就是public的方法,供外部使用,通过这些public的方法,可以操作内部数据,所以称之为接口。 封装,一个类是由数据与方法组成的,将数据和方法放在一起,就是封装。
知识点补充:
I).构造函数后面的冒号就是初始化,而括号里面的等于号并不是初始化,而是变量生成以后的赋值而已(永远都是2个步骤)
II) 作用域符号 :: 的前面一般是类名称,后面一般是该类的成员名称,C++为例避免不同的类有名称相同的成员而采用作用域的方式进行区分
如:A,B表示两个类,在A,B中都有成员member。那么 A::member就表示类A中的成员member B::member就表示类B中的成员member
III) 全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用 :: 来区分如:
char zhou; //全局变量
void sleep()
{
char zhou; //局部变量
char(局部变量) = char(局部变量) *char(局部变量) ;
::char(全局变量) =::char(全局变量) *char(局部变量);
}
IV) :: 是C++里的“作用域分解运算符”。
比如声明了一个类A,类A里声明了一个成员函数voidf(),但没有在类的声明里给出f的定义,那么在类外定义f时,就要写成voidA::f(),表示这个f()函数是类A的成员函数。例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1.对原功能的代码在打包一层
由于我们不想暴露AImpl.h中,故对其封装,即在A.h和A.cpp中对其包一层。
我们甚至在A.h中都不需要声明 AImpl *imp_的,在A.h中可以只声明一个void *imp_,在A.cpp中将该void *指针转换成Almpl *即可。
AImpl.h
class AImpl
{
public:
...
private:
int a;
...
};
A,h
class AImpl;
class A
{
public:
A();
~A();
void f();
private:
AImpl *imp_; //
};
A.CPP
#include "AImpl.h"
#include "A.h"
A::A()
: imp_(new AImpl)
{
}
A::~A()
{
delete imp_;
}
void A::f()
{
imp_->f();
}
2.父类声明为虚函数
下面通过一个具体的实例进行接口的是实现。
base.h
#ifndef BASE_H
#define BASE_H
#include "ibase.h"
class CBase :
public Ibase
{
public:
CBase(void);
virtual ~CBase(void);
virtual void Release();
virtual void Do();
private:
int m_nID;
};
#endif // BASE_H
ibase.h
#ifndef IBASE_H
#define IBASE_H
class Ibase{
public:
Ibase(void);
virtual ~Ibase(void); //虚函数,meiyou7函数体
virtual void Release()=0;//纯虚函数需要加=0
virtual void Do()=0;
};
Ibase *Create();
#endif // BASE_H
base.cpp
#include "ibase.h"
#include "base.h"
using namespace std;
CBase::CBase(void)
{
m_nID=0;
}
CBase::~CBase(void)
{
}
void CBase::Release()
{
delete this;
}
void CBase::Do()
{
m_nID++;
}
ibase.cpp
#include "ibase.h"
#include "base.h"
//实现
Ibase::Ibase()
{
//构造函数
}
Ibase::~Ibase()
{
//析构函数
}
Ibase *Create()
{
return new CBase;
}
main,cpp
#include
#include "ibase.h"
using namespace std;
int main()
{
Ibase *poBase=Create();
if(NULL!=poBase)
{
poBase->Do();
poBase->Release();
poBase=NULL;
}
return 0;
}
在一个类中,可以利用关键字friend将别的模块(一般函数、其他类的成员函数或其他类)声明为它的友元,这样这个类中本来隐藏的信息就可以被友元访问j。如果友元是一般函数或类的成员函数,称为友元函数;如果友元是一个类,则称为友元类,友元类的所有成员函数都成为友元函数。
class B
{
//B类的成员声明
friend float f(B&x,B&y);//友元函数声明
friend class A;//声明A为B的友元类
};
在B类声明f函数为友元函数,则在f函数中通过对象名可直接访问B类所有的数据成员。同时在B类声明A类为友元类,则A类的所有成员函数都是B类的友元函数,都可以访问B类的私有和保护成员。采用友元类共享数据机制,使友元类成员函数可以通过对象名直接访问到隐藏的数据,从而使程序达到高效协调工作。在较为复杂的问题中,实现不同类之间的数据共享,友元类的使用也是必不可少的选择。友元在类之间、类与普通函数之间共享了内部封装的数据的同时,必然会对类的封装性带来一定的破坏。因此在程序设计中使用友元,要在共享和封装之间找到一个恰当的平衡点,从而达到提高程序效率同时,将程序隐患降来最低。
C++中使用静态成员可以实现同一类的不同对象之间共享数据 j。类的普通数据成员在类的每一个对象都有一个拷贝,就是说每个对象的同名数据成员可以分别存储不同数值,这就保证对象拥有自身区别其他对象的特征的需要。静态数据成员是类的数据成员的一种特例,采用static关键字来声明;每个类只有一个拷贝,由该类的所有对象共同维护和使用,从而实现了同一类的不同对象之间的数据共享。
例如:
#include
using namespace std;
class sample
{
private:
static char m_sArray[10];
public:
sample(){ cout << "default constructor! "<< endl;}
sample(sample & s){ cout <<"copy constructor! " << endl;}
void show(void) { cout << m_sArray << endl;}
void input(void) { cin.get(m_sArray, 10);}
};
char sample::m_sArray[10] = "I am a engineer";
int main(void)
{
sample e1;
e1.show();
sample e2(e1);
e2.show();
e2.input();
e1.show();
}
//运行结果如下:
default constructor!
I am a engineer
copy constructor!
I am a engineer
this is my job
this is my job
静态成员变量m_sArray确实起到了在不同对象间共享的作用!不过由于其是静态属性,内存是在全局/静态区域开辟的,属于栈内存区,内存大小使用受限。如果能动态从堆中申请内存,则可以使用大内存空间了。
有一学生类:
class engineer
{
Private:
im ID :
char nalne;
static count;//静态数据成员,用来存放“人数”
string name;
}
如果程序中需要统计学生人数,这个数据存放在什么地方呢?若以类外的全局变量来存放,不能实现数据的隐藏,若在类中增加一个数据成员用以存放人数,必然在每一个对象中都存储一副本,这样不仅冗余,而且每个对象分别维护一个“人数”,势必造成数据的不一致性。因此,比较好的方案是在engineer类中增加一个静态数据成员。static count用来存放学生“人数”。