Bootstrap

类与对象(三)

类的其他成员

常成员
静态成员
友元

常成员

常数据成员

常数据成员是指数据成员在实例化被初始化后约束为只读

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();
}
;