静态与友元
封装性:对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作与访问。
类外如何访问被封装的成员(private 和 protected成员)?
–通过对象名.公有成员函数
良好的封装性,考虑角度: 将对象的成员变量与实现细节隐藏起来,不允许外部访问。
把方法暴露出来,让方法来控制对这些成员变量进行安全的访问与操作。
例6.1 使用普通函数计算圆环面积
#include <iostream>
using namespace std;
class Circle{
public:
Circle(double a=0.0) //带默认参数值的构造函数
{ r=a; }
double Area()
{return 3.14*r*r;}
private:
double r;
};
double AreaRing(Circle &c1,Circle &c2)//普通函数
{ return c1.Area()-c2.Area();//通过对象.成员函数
}
//由于封装,类外无法访问私有数据成员,只能通过公有函数调用
int main()
{
Circle c1(10),c2(5);//定义圆类对象
cout<<AreaRing(c1,c2)<<endl;//调用普通函数
return 0;
}
例6.2 全局变量使用示例
统计Circle类被创建对象的次数
#include <iostream>
using namespace std;
int total=0;//使用全局变量
class Circle{
public:
Circle(double a=0) //带默认参数值的构造函数
{ r=a;total++; }
Circle(Circle &x)//拷贝构造函数
{ r=x.r;
total++;
}
double Area()
{return 3.14*r*r;}
private:
double r;
};
int main()
{
Circle c1(10),c2(5),c3,c4(c1);//定义圆类对象
cout<<"定义对象的个数="<<total<<endl;//通过全局变量输出定义对象的个数
return 0;
}
Static的使用
Static:(静态)类数据和(静态)类函数
类中有一种特殊的数据成员或成员函数,它不属于某个对象,不能通过某个对象来引用,在声明前加上static关键字。
不论定义多少个对象,在内存中,静态的数据备份只有1份。
静态数据可以被所有的对象共享,生命周期从创建开始到程序运行结束。
C++一个类中的四种成员
静态数据成员和非静态数据成员、静态函数和非静态函数。
①非静态数据成员被放在每一个对象体内作为对象专有的数据成员。
②静态数据成员被提取出来放在程序的静态数据区内,为该类所有对象共享,因此只存在一份。
③静态和非静态成员函数最终都被提取出来放在程序的代码段中并为该类所有对象共享,因此每一个成员函数也只能存在一份代码实体。在c++中类的成员函数都是保存在静态存储区中的,那静态函数也是保存在静态存储区中的,他们都是在类中保存同一个备份。
因此,构成对象本身的只有数据,任何成员函数都不隶属于任何一个对象,非静态成员函数与对象的关系就是绑定,绑定的中介就是this指针。成员函数为该类所有对象共享,不仅是出于简化语言实现、节省存储的目的,而且是为了使同类对象有一致的行为。同类对象的行为虽然一致,但是操作不同的数据成员。
静态机制-同类的所有对象共享
静态成员:static
static所修饰的数据成员为静态数据成员(即类数据成员),所修饰的成员函数为静态成员函数(即类成员函数)。
静态数据成员是同类的所有对象共享的成员,而不是某一对象的独有的成员,它的值对每个对象都是一样的。对静态数据成员的值的更新,即是对所有对象的该静态数据成员值的更新。
静态数据成员初始化须在类的外部进行,与一般数据成员初始化不同,它的格式如下:
<数据类型><类名>::<静态数据成员名>=<值>
静态数据成员可被该类的静态成员函数使用,也可被该类的普通成员函数使用。
使用示例1
#include <iostream>
using namespace std;
class Circle{
public:
Circle(double a=0) //带默认参数值的构造函数
{ r=a;total++; }
Circle(Circle &x)//拷贝构造函数
{ r=x.r;
total++;
}
double Area()
{return 3.14*r*r;}
static int total;//定义静态数据成员
private:
double r;
};
int Circle::total=0;
int main()
{
Circle c1(10),c2(5),c3,c4(c1);//定义圆类对象
cout<<"定义对象的个数="<<Circle::total<<endl;//通过静态成员输出定义对象的个数
return 0;
}
使用实例2
#include <iostream>
using namespace std;
class Circle{
public:
Circle(double a=0) //带默认参数值的构造函数
{ r=a;total++; }
Circle(Circle &x)
{ r=x.r;
total++;
}
double Area()
{return 3.14*r*r;}
int GetTotal()//静态成员可以被该类的普通成员函数使用。
{
return total;
}
private:
double r;
static int total;//定义静态数据成员
};
int Circle::total=0;
int main()
{
Circle c1(10),c2(5),c3,c4(c1);//定义圆类对象
cout<<"定义对象的个数="<<c1.GetTotal()<<endl;//如果没有定义任何对象,就无法访问类数据了
return 0;
}
静态成员用下划线表示
静态成员函数
- 静态成员函数和静态数据成员一样,它们都属于类的静态成员,但它们都不是某一对象的成员。
- 类外代码可以使用类名和作用域操作符来调用静态成员函数。
- 静态成员函数只能引用属于该类的静态数据成员或静态成员函数。
例6.5 静态成员函数使用示例
#include <iostream>
using namespace std;
class Circle{
public:
Circle(double a=0) //带默认参数值的构造函数
{ r=a;total++; }
Circle(Circle &x)//拷贝构造函数定义
{ r=x.r;
total++;
}
double Area()
{return 3.14*r*r;}
static int GetTotal()
{
return total;
}
private:
double r;
static int total;//定义静态数据成员
};
int Circle::total=0;
int main()
{
Circle c1(10),c2(5),c3,c4(c1);//定义圆类对象
cout<<"定义对象的个数="<<Circle::GetTotal()<<endl;//如果没有定义任何对象,也可以通过Circle::GetTotal()访问类数据
return 0;
}
类外不同对象之间数据共享:友元机制
友元—friend
友元是C++提供的一种破坏数据封装和数据隐藏的机制。 通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。
可以使用友元函数、友元成员函数和友元类。 为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。
友元函数
友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问 private 和 protected成员
作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择。
访问对象中的成员必须通过对象名。
注意:一个函数可以是多个类的友元函数,需要在各个类中分别声明,并且友元关系不具有传递性。
例6.6 使用友元函数计算圆环面积
#include <iostream>
using namespace std;
class Circle{
public:
Circle(double a=0.0) //带默认参数值的构造函数
{ r=a; }
double Area()
{return 3.14*r*r;}
friend double AreaRing(Circle &c1,Circle &c2);//友元函数声明
//注意:友元关系是单向的
private:
double r;
};
double AreaRing(Circle &c1,Circle &c2)//普通友元函数
{ return 3.14*c1.r*c1.r-3.14*c2.r*c2.r;//通过友元函数访问私有数据成员
}//由于友元函数打破了封装,类外可以访问私有数据成员
int main()
{
Circle c1(10),c2(5);//定义圆类对象
cout<<AreaRing(c1,c2)<<endl;//调用友元函数
return 0;
}
友元成员函数
若一个类的成员函数是另一个类的友元,则称该成员函数为另一个类的友元成员函数。
声明语法:将某个类的成员函数在另一个类中使用friend修饰说明,并且还加上成员函数所在的类名。
例6.7:使用友元成员函数输出圆信息
#include <iostream>
using namespace std;
class Point;//前向引用声明
class Circle{
public:
Circle(double a=0.0) //带默认参数值的构造函数
{ r=a; }
double Area()
{return 3.14*r*r;}
void Show(Point &p);//该函数是Circle的成员函数,Point类的友元函数
private:
double r;
};
class Point
{private:
double x,y;
public:
Point(double a=0.0,double b=0.0)
{ x=a;y=b; }
void Show()
{ cout<<x<<","<<y<<endl; }
double GetX()
{return x;}
double GetY()
{return y;}
friend void Circle::Show(Point &p);//友元函数声明
};
void Circle::Show(Point &p)
{
cout<<"半径="<<r<<endl<<"圆心=";
cout<<p.x<<","<<p.y<<endl;//等价于p.Show();
}int main()
{
Circle c(10);//定义圆类对象
Point p(100,100);//定义点类对象
c.Show(p);//调用友元成员函数
return 0;
}
前向引用声明
类应该先声明,后使用。
如果需要在某个类的声明之前,引用该类,则应进行前向引用声明。
前向引用声明只为程序引入一个标识符,但具体声明在其它地方。
友元类
若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。
//从这个角度来看,友元类的成员函数都是另一个类的友元成员函数
声明语法:将友元类名在另一个类中使用friend修饰说明。
#include <iostream>
using namespace std;
class Point;//前向引用声明
class Circle{
public:
Circle(double a=0.0) //带默认参数值的构造函数
{ r=a; }
double Area()
{return 3.14*r*r;}
void Show(Point &p);
private:
double r;
};
class Point
{private:
double x,y;
public:
Point(double a=0.0,double b=0.0)
{ x=a;y=b; }
void Show()
{ cout<<x<<","<<y<<endl; }
double GetX()
{return x;}
double GetY()
{return y;}
friend class Circle;//友元类声明,意味着Circle类的所有成员函数都是Point类的友元成员函数
};
void Circle::Show(Point &p)
{
cout<<"半径="<<r<<endl<<"圆心=";
cout<<p.x<<","<<p.y<<endl;//等价于p.Show();
}
int main()
{
Circle c(10);//定义圆类对象
Point p(100,100);//定义点类对象
c.Show(p);//调用友元类的成员函数
return 0;
}
常类型-共享数据保护
- 常引用:被引用的对象不能被更新。
const 类型说明符 &引用名 - 常对象:必须进行初始化,不能被更新。
类名 const 对象名 或const 类名 对象名 - 常成员:用const修饰的类成员
例6.9 常引用做形参
#include <iostream>
using namespace std;
void display(const double& r);
int main( )
{ double d(9.5);
display(d);
return 0;
}
void display(const double& r)
//常引用做形参,在函数中不能更新 r所引用的变量。
{ cout<<r<<endl; }
//注意:常对象引用用法与常对象一样,只能调用常成员函数。
例6.10:常对象举例
凡希望保证数据成员不被改变的对象,可以声明为常对象。
#include <iostream> using namespace std; class A { public: A(int i,int j) {x=i; y=j;} void print( ); void print( ) const; private: int x,y; }; void A::print ( ) { cout<<"普通成员函数:"<<x<<","<<y<<endl; } void A::print ( ) const { cout<<"常成员函数:"<<x<<","<<y<<endl; }//通过常对象只能调用它的常成员函数 int main() { A const a(3,4); //a是常对象,不能被更新 a.print(); return 0; }
用const修饰的对象成员
常成员函数:使用const关键字说明的函数。
常成员函数不更新对象的数据成员。
常成员函数说明格式:类型说明符 函数名(参数表)const;这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字。
const关键字可以被用于参与对重载函数的区分
常数据成员
使用const说明的数据成员。
普通成员函数可以调用常数据成员
例6.11 常数据成员举例
#include <iostream>
using namespace std;
class A
{public:
A(int i,int j);
void print( );
private:
const int a;//常数据成员a
int b;//普通数据成员b
};
A::A(int i,int j):a(i) //构造函数
//常成员只能通过初始化列表给对象的常数据成员a赋初值
{ b=j;}
void A::print()
{ cout<<a<<","<<b<<endl; }//普通成员函数可以调用常数据成员
int main( )
{//建立对象a1和a2,并以100和0作初值
A a1(100,10),a2(0,2);
a1.print();
a2.print();
return 0;
}