c++初级 之 类的“嵌套” 及 常成员函数和常数据成员

本文介绍了C++中的类嵌套概念,即一个类包含另一个类的头文件,允许在外部类中直接创建内部类对象并调用其成员。同时讲解了常成员函数和常数据成员,通过点类和线类的例子进行阐述,包括Coordinate.h、Coordinate.cpp、Line.h、Line.cpp和测试程序demo.cpp的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.类的“嵌套”:当类A包含类B的头文件时,就像类B被嵌套在类A中了,在类A中可以定义类B的对象并调用对象的公有成员。

2.常成员函数和常数据成员:在点类中举例说明。

以点类和线类为例:

点类

Coordinate.h

#ifndef COORDINATE_H
#define COORDINATE_H
class Coordinate
{
public:
	//构造函数
	Coordinate(int X = 0,int Y = 0);//默认值只需在声明时写出,定义时不用写
	Coordinate(const Coordinate &coor);//拷贝构造函数

	//数据封装函数
	void setX(int _x);
	void setY(int _y);//与下面同名。则对象变量会默认调用上面这个,如果没有上面这个,就会调用下面的。
	void setY(int _y)const;//常成员函数。对象常量只能调用公有的常成员函数,其他非常的不可调用,所以调用下面的。
	                       //常成员函数等价于void setY(const Coordinate* this,int _y);则当前对象(*this)不可修改
	//一般功能函数
	int getX();
	int getY();
	int getX()const;//const的Coordinate类对象(对象常量),只能调用有const修饰的函数
	int getY()const;//如果没有上面的int getY(),那么对象变量也可以调用这个int getY()const
	  
	//析构函数
	~Coordinate();

private:
	const int m_iX; //常数据成员,一旦被赋初值(构造函数中)就不能再改变。
	int m_iY;
};

#endif



Coordinate.cpp

#include"Coordinate.h"
#include<iostream>
using namespace std;

//构造函数(不用加const,常对象初始化时也会调用)
Coordinate::Coordinate(int X,int Y):m_iX(X)
{
	//m_iX = X;常成员函数只能用列表初始化
	m_iY = Y;
	cout << "Coordinate(int X,int Y) " << m_iX << ","<< m_iY << endl;
}

Coordinate::Coordinate(const Coordinate &coor):m_iX(coor.m_iX)
{
	//m_iX = coor.m_iX;
	m_iY = coor.m_iY;
	cout << "Coordinate(const Coordinate &coor)" << m_iX << ","<< m_iY << endl;
}

//数据封装函数
void Coordinate::setX(int _x)
{
	//m_iY = _x;//报错
	cout << "常数据成员不能改变值" << endl;
}
void Coordinate::setY(int _y)
{
	m_iY = _y;
	cout << "等价于void setY(Coordinate * this,int _y)" << endl;

}

void Coordinate::setY(int _y)const//常成员函数
{
	//m_iY = _y;//报错
	cout << "常成员函数不能修改对象的数据成员的值" << endl;
	cout << "等价于void setY(const Coordinate * this,int _y)" << endl;
}

int Coordinate::getX()
{
	return m_iX;
}
int Coordinate::getY()
{
	return m_iY;
}

int Coordinate::getX()const
{
	//this->setX(2);常成员函数内部不能调用普通的成员函数(因为此时对象是个常量,无法调用没有const修饰的函数)
	return m_iX;
}
int Coordinate::getY()const
{
	return m_iY;
}

//析构函数(不用加const,常对象初始化时也会调用)
Coordinate::~Coordinate()
{
	cout << "~Coordinate()" << m_iX << ","<< m_iY <<endl;
}

线类

Line.h

#ifndef LINE_H
#define LINE_H
#include"Coordinate.h"

class Line
{
public:
	//构造函数
	Line();
	Line(int x1,int y1,int x2,int y2);
	Line(const Coordinate &A,const Coordinate &B);

	//数据封装函数
	void setA(int _x1,int _y1);
	void setB(int _x2,int _y2);

	//一般功能函数
	void printline();

	//析构函数
	~Line();
private:
	Coordinate m_CoorA;  //取名一定要规范!!m_[类型][名字]。
	Coordinate m_CoorB;
};

#endif

Line.cpp

#include"Line.h"
#include<iostream>
using namespace std;
//构造函数
Line::Line()
{
	cout << "Line()" << endl;
}

//这里用set函数或者初始化列表都可以,我选择初始化列表
//因为如果Coordinate类的构造函数的所有参数必须赋值(即没有默认值;即没有默认构造函数)的话,那就只能用初始化列表的方式了,
//因为实例化Line类对象时,先会实例化两个Coordinate类的对象而且是没有赋初值的!
//若直接用set函数,是会报错的因为没有赋初值就没有真正被实例化并分配内存,此时只有通过Line构造函数的初始化列表给它们赋初值,使之真正实例化。
//Line::Line(int x1,int y1,int x2,int y2)
//{
//	m_CoorA.setX(x1);
//	m_CoorA.setY(y1);
//	m_CoorB.setX(x2);
//	m_CoorB.setY(y2);
//	cout << "Line(int x1,int y1,int x2,int y2)" << endl;
//}

Line::Line(int x1,int y1,int x2,int y2):m_CoorA(x1,y1),m_CoorB(x2,y2)
{
	cout << "Line(int x1,int y1,int x2,int y2)" << endl;
}

Line::Line(const Coordinate &A,const Coordinate &B):m_CoorA(A),m_CoorB(B)
{
	/*m_CoorA = A;因为m_CoorA和B里都有常数据成员,不可直接赋值,只能用列表初始化
	m_CoorB = B;*/
	cout << "Line(Coordinate A,Coordinate B)" << endl;
}

//数据封装函数
void Line::setA(int _x1,int _y1)
{
	m_CoorA.setX(_x1);
	m_CoorA.setY(_y1);
}
void Line::setB(int _x2,int _y2)
{
	m_CoorB.setX(_x2);
	m_CoorB.setY(_y2);
}

//一般功能函数
void Line::printline()
{
	cout << "(" << m_CoorA.getX() << "," << m_CoorA.getY() << ")" <<endl;
	cout << "(" << m_CoorB.getX() << "," << m_CoorB.getY() << ")" <<endl;
}

//析构函数
Line::~Line()
{
	cout << "~Line()" << "(" << m_CoorA.getX() << "," << m_CoorA.getY() << ") ";
	cout << "(" << m_CoorB.getX() << "," << m_CoorB.getY() << ")" <<endl;
}

测试程序

demo.cpp

#include<iostream>
#include<stdlib.h>
#include"Coordinate.h"
#include"Line.h"
using namespace std;

int main()
{
	//点实例化
	Coordinate p1(3,4);
	Coordinate p2;
	Coordinate p3(8);
	Coordinate *pp = new Coordinate();//加不加括号都对,堆内申请
	delete pp;
	Coordinate *PP2 = &p2;//栈内申请
	//常对象测试
	const Coordinate p4(1,1);
	p4.setY(3);        //调用的会是带const字样的setY,因为p4是常对象,只能调用常成员函数
	//p4.setX(1);//报错,因为setX非常成员函数
	cout << "(" <<p4.getX() << "," <<p4.getY() << ")" << endl;
	//调用封装函数
	p2.setX(3);
	p2.setY(7);
	//线段实例化
	Line *p = new Line(p1,p2);
	Line l1(0,0,6,8);
	Line l2;
	//调用封装函数
	l2.setA(1,2);
	l2.setB(3,3);
	//打印功能
	l1.printline();
	p->printline();
	l2.printline();
	delete p;
	p = NULL;
	system("pause");
	return 0;
}//结果比想象的多了两个析构函数,不懂。。已解决,修改了构造函数从Line(Coordinate A,Coordinate B)变成了Line(const Coordinate &A,const Coordinate &B)。
//类似于拷贝函数。改前,调用构函时,先会构造A、B这两个形参来接收p1、p2的值(而且是调用两次拷贝构造函数给AB赋值),Line构函返回后AB被释放,所以会出现两次析构函数。
//而加了const(用于保护原引用对象不被改变)和引用&后,引用是对象的别名,不会安排新内存构造新对象作形参,所以就没有多出来的两个构造函数和析构函数了。




结果如下:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值