类的其他成员
常成员
静态成员
友元
常成员
常数据成员
常数据成员是指数据成员在实例化被初始化后约束为只读。
const int m;
static const int b; //静态常数据成员,只能初始化,不能赋值;
常对象
常成员函数是指成员函数的this指针被约束为指向常量的常指针,函数体内不能修改数据成员的值。
常成员函数
调用的时候有个一一对应关系
int main()
{R a(5,4);
a.print();//调用void print()
const R b(20,52);
b.print();//调用void print() const
}
常成员函数不能更新对象的数据成员
也不能调用该类中的非常成员函数(下方代码可说明)
#include<iostream>
using namespace std;
class R
{
public:
R(int r1, int r2) { R1 = r1; R2 = r2; }
void print () const;
void print()
{
cout << "a" << endl;
}
private:
int R1, R2;
};
void R::print() const
{
cout << R1 << ":" << R2 << endl;
print();//由于无论是不是常成员函数,因为它俩同名,又因为常成员函数里不能调用非常成员函数,同上。
//因此这儿应该是一直在调用该非常成员函数,在无穷递归
}
int main()
{
const R a(5, 4);
a.print();//调用void print() const
return 0;
}
常对象只能调用常成员函数(上方代码可说明)
但是常成员函数也可以被普通对象来调用(下方代码可说明)
当函数中只有常成员函数的时候
即使是普通对象
也可调用常成员函数
#include<iostream>
using namespace std;
class R
{
public:
R(int r1, int r2) { R1 = r1; R2 = r2; }
void print () const;
private:
int R1, R2;
};
void R::print() const
{
cout << R1 << ":" << R2 << endl;
}
int main()
{
R a(5, 4);
a.print();//调用void print() const
return 0;
}
#include<iostream>
using namespace std;
class A
{
int a;
public:
void f(int m)const;
};
void A::f(int m)const
{
m = a;//只有这个是对数据成员的读操作,其他都是写操作
//a = m; 报错
//cin>>a;
//m=a++;
}
静态成员
函数声明好像一直都还挺无所谓的,就算在函数体里说明了,在不在类的花括号外写,好像没什么太大区别。
类成员冠以static声明时,称为静态成员。
静态数据成员为同类对象共享。
静态成员函数与静态数据成员协同操作。
静态成员函数主要是用来修改静态成员的。
把一个类的成员说明为 static 时,这个类无论有多少个对象创建,这些对象共享这个 static 成员。
静态成员局部于类,它不是对象成员。
静态数据成员
在类的声明中仅仅对静态数据成员进行引用性说明,必须在文件作用域的某个地方使用类名限定进行定义性说明,这时也可初始化。
在类的声明中只能声明静态成员数据的存在。由于类的声明是抽象的,静态成员数据的初始化需要在类的外部进行,通过类名对它进行访问。
#include<iostream>
using namespace std;
class a
{
public:
a();
~a();
int x, y;
static int num;
void print();
};
int a::num;//如果把这一行注释掉,那么会出现下方的报错
//这一块儿的静态数据成员在类外的再次定义是必须要有的,就算不进行初始化,也要有定义,否则就报错
a::a()
{
cout << "contructed" << endl;
}
a::~a()
{
cout << "deleted" << endl;
}
void a::print()
{
cout << x << endl << y << endl << num << endl;
}
int main()
{
a b1;
b1.x = 3;
b1.y = 4;
b1.num = 5;
b1.print();
return 0;
}
如果创建了两个类对象,a和b,它们都有共同的数据成员num,先令a.num=1;再令b.num=2;然后分别输出a.num和b.num;结果是2 2。
私有静态数据成员可以借用公有成员函数间接使用与改变;
公有静态数据成员可以直接使用。
两种不同的访问方式:
静态成员函数
成员函数可分为静态成员函数和非静态成员函数。
因此,并非所有的成员函数都有this指针,像静态成员函数就没有,尽管它也叫成员函数。
静态成员函数数冠以关键字static。
静态成员函数没有this指针,但是是成员函数(参照上面几句话),只能对静态数据操作。
在类外调用静态成员函数用 “类名 :: ”作限定词,或通过对象调用。
友元
由于一个对象的私有成员只能通过成员函数访问,定义公有数据又破坏了信息的隐蔽性。
友元是对类操作的辅助手段。友元能够引用类中本来被隐蔽的信息。
C++提供了一种辅助手段,定义类的友元。友元可以访问类的所有成员,包括私有成员。友元可以是一个函数或者是一个类。
使用友元目的是基于程序的运行效率;
运算符重载的某些场合需要使用友元。
存在非对称性和非传递性。
缺少的代码,友元函数对应友元函数,友元类对应普通成员函数。
友元函数
友元函数不是类的成员,属于非成员函数, 一般通过参数访问对象的私有成员。
#include<iostream>
using namespace std;
class line;
class box
{
private:
int color;
int upx, upy;
int lowx, lowy;
public:
//这儿缺了一个友元函数的声明
//导致下面的友元函数的声明就只能访问line类的私有成员
//因为它只是line类的友元函数
//所以看到下面友元函数的定义里有box的私有函数,就明白这儿缺个友元
void set_color(int c) { color = c; }
void define_box(int x1, int y1, int x2, int y2)
{
upx = x1; upy = y1; lowx = x2; lowy = y2;
}
};
class line
{
private:
int color;
int startx, starty;
int endx, endy;
public:
friend int same_color(line l, box b);
void set_color(int c) { color = c; }
void define_line(int x1,int x2,int y1,int y2)
{
startx = x1; starty = y1; endx = x2; endy = y2;
}
};
int same_color(line l, box b)
{
if (l.color == b.color) return 1;
return 0;
}
友元类
若F类是A类的友元类,则F类的所有成员函数都是A类的友元函数。
友元类通常设计为一种对数据操作或类之间传递消息的辅助类。
这种非对称性,可以用一个例子来说明:
#include<iostream>
using namespace std;
class A
{
/*friend class B;*/
public:
void Display()
{
cout << x << endl;
}
private:
int x;
};
class B
{
public:
void Set(int i);
void Display();
private:
A a;
};
void B::Set(int i)
{
a.x = i;//报错,不能访问a的私有成员x,即使a是B的类类型数据成员对象
//因为像在主函数里不能访问a的私有成员对象一样,
// **即使是在B类的函数体里这对于A类对象a来说都属于类外**
}
void B::Display ()
{
a.Display ();
}
这么干就会报错,在没有继承的情况下,
友元的存在,使B成为A的友元,也就使B可以访问A的任何成员。
#include <iostream>
#include <cmath>
using namespace std;
class Point
{
private:
double x, y;
//这儿缺了一个友元类
//friend class Line
public:
Point(double i = 0, double j = 0)
{
x = i; y = j;
}
Point(Point& p)
{
x = p.x; y = p.y;
}
};
class Line
{
private:
Point p1, p2;//
public:
Line(Point& xp1, Point& xp2) :p1(xp1), p2(xp2) {}
double GetLength();
};
double Line::GetLength()
{
double dx = p2.x - p1.x;
double dy = p2.y - p1.y;
return sqrt(dx * dx + dy * dy);
}
void main()
{
Point p1, p2(6, 8);
Line L1(p1, p2);
cout << L1.GetLength() << endl;
}
类的包含
称为has-a
是一种软件重用技术,在定义一个新类的时候,通过编译器把另一个类“抄写”进来,这样 程序员就不用再编写一模一样的代码,只要添加新的功能代码即可。
这儿之所以不报错,是因为虽然A a是A类类对象,但是x在A类里的访问特性是公有的,所以也就是说,B类相当于A类是在类外,因为这个公有的特性,所以即使这儿不用友元,也是可以访问不会报错,前面讲友元的时候要用,友元类是因为,x是私有成员,所以本类中的数据成员是公有还是私有要看清吧!
但是,要在类的包含里面实际使用a的数据成员,就需要初始化式来对数据成员进行一个初始化,可以是复制构造函数,也可以是普通的构造函数。
格式大致如下:
class A
{
public:
A(int x):a(x){}
int a;
};
class B
{
public:
B(int x,int y):aa(x)//类的对象名后面的形式和本类中构造函数的格式大致相同,括号里面的参数都是一样的
{
b=y;
}
void out()
{
cout<<"aa="<<aa.a<<endl<<"b="<<b<<endl;}
private:
int b;
A aa;
};
int main()
{
B objB(3,5);
objB.out();
}