网站搭建工作室加盟,网站建设贰金手指下拉,凡客诚品老板,网站做电子链接标识申请好吗对象与对象数组
实验介绍
本章节主要介绍对象数组和对象成员。在实际的开发中#xff0c;对象数组和对象成员是经常使用的#xff0c;所以首先需要学习对象数组与对象成员的各种使用方法。
提示#xff1a;为了方便课程讲解#xff0c;示例代码使用类内定义的方式实现对象数组和对象成员是经常使用的所以首先需要学习对象数组与对象成员的各种使用方法。
提示为了方便课程讲解示例代码使用类内定义的方式实现如果自己动手做实验的时候希望能够使用分文件类外定义的方式来编写代码。
知识点
对象数组 实例化对象数组堆上操作对象数组 对象成员 构造和析构顺序初始化对象成员
对象数组 假设定义了一个学生类现在要实例化一个班的学生如果逐个对学生进行实例化操作那肯定是非常麻烦的这时使用对象数组就能很方便的完成编写。假设有一个点类如果实例化一个矩形也可以使用对象数组的方式。 点类 - 示例代码 1
定义一个点类在本小节以后的示例代码中都是用该类在以下的示例代码中尽量使用之前学到过的知识点。
为了方便查看运行结果分别在构造函数、拷贝构造函数和析构函数中打印函数的名称。
#include iostream
using namespace std;class Point
{
public:// 使用带参数默认构造函数并使用初始化列表初始化 xyPoint(double x 0, double y 0) : x(x), y(y) {//cout Point(double x 0, double y 0) endl;cout Point(double x x , double y y ) endl;}// 拷贝构造函数Point(const Point p) {//cout Point(const Point p) endl;// 打印点的值cout Point(const Point p:( p.x , p.y ) endl;this-x p.x;this-y p.y;}// 析构函数由于没有申请内存析构函数中不需要做什么~Point() {//cout ~Point() endl;cout ~Point():( x , y ) endl;}// x, y 绑定的成成员函数void setPoint(const Point p) {this-x p.x;this-y p.y;}void setPoint(double x, double y) {this-x x;this-y y;}void setX(double x) { this-x x; }void setY(double y) { this-y y; }double getX() { return x; }double getY() { return y; }
private:double x;double y;
};
栈上实例化
示例代码 2 为了效果演示示例代码将对象数组定义在一个函数中可以在函数执行完之后调用对象数组的析构函数。 // 栈上实例化
void stackInstantiation()
{// 实例化对象数组Point point[3];// 对象数组操作cout p[0]: ( point[0].getX() , point[0].getY() ) endl;cout p[1]: ( point[1].getX() , point[1].getY() ) endl;cout p[2]: ( point[2].getX() , point[2].getY() ) endl;point[0].setPoint(3, 4);cout p[0]: ( point[0].getX() , point[0].getY() ) endl;
}int main()
{stackInstantiation();return 0;
}
运行结果 栈上实例化对象数组小结
实例化对象数组时每一个对象的构造函数都会被执行。系统自动销毁栈上对象数组并且销毁对象数组时每一个对象析构函数都会被执行。访问对象数组时使用 [ i ] 的方式访问相应位置的对象。建议将类数据成员都初始化可以使用默认值初始化。void setPoint(const Point p) // 如果是自定义类作为参数时建议使用引用的方式传入参数如果该参数在函数中无需修改且没有输出建议加上 const。
示例代码 3
void stackInstantiation()
{Point point[3];Point *p point;cout p: ( p-getX() , p-getY() ) endl;p;cout p: ( p-getX() , p-getY() ) endl;p;cout p: ( p-getX() , p-getY() ) endl;point[2].setPoint(3, 4);cout p: ( p-getX() , p-getY() ) endl;
}int main()
{stackInstantiation();return 0;
}
运行结果试验中声明的是对象数组但是数组其本身也是可以当做指针使用。 堆上实例化 在堆上操作对象数据会比在栈上操作对象数组复杂但却比栈上操作更加的灵活如果数据量比较大建议在堆上操作。 示例代码 4
int main()
{// 堆上实例化对象数组Point *point new Point[3];cout p[0]: ( point[0].getX() , point[0].getY() ) endl;cout p[1]: ( point[1].getX() , point[1].getY() ) endl;cout p[2]: ( point[2].getX() , point[2].getY() ) endl;point[0].setPoint(3, 4);cout p[0]: ( point[0].getX() , point[0].getY() ) endl;// 释放内存delete [] point;point nullptr;return 0;
}
运行结果按照示例代码 3 中的访问方式与栈上访问方式是一样的跟栈上访问的结果也是一样的。但是别急后面还有堆上特有的操作。 示例代码 5 在堆上操作对象数据会比在栈上操作对象数组复杂但却比栈上操作更加的灵活如果数据量会比较大建议在堆上操作。 // 堆上实例化
int main()
{// 实例化对象Point *p new Point[3];Point *point p;cout point: ( point-getX() , point-getY() ) endl;p;cout point: ( point-getX() , point-getY() ) endl;p;cout point: ( point-getX() , point-getY() ) endl;point-setPoint(3, 4);cout point: ( point-getX() , point-getY() ) endl;cout p: ( p-getX() , p-getY() ) endl;// 释放内存delete [] point;point nullptr;return 0;
}
运行结果发现使用指针的方式一样可以访问对象数组但是使用时也要注意几个问题。
使用 - 的方式来访问类成员函数并且不需要使用下标。Point *point p; 可以发现我又重新声明一个指针因为一个指针只能指向一个对象通过指针 或者 -- 运算符的方式来访问对象数组中对象。 示例代码 6 强调堆上申请空间与释放空间的问题请注意一下代码与之前的异同之处在销毁对象数组时使用的是 delete point; 而在之前的示例代码中使用的是 delete [] point; 来销毁对象数组的。 // 堆上实例化
int main()
{// 实例化对象Point *point new Point[3];cout point: ( point-getX() , point-getY() ) endl;point;cout point: ( point-getX() , point-getY() ) endl;point;cout point: ( point-getX() , point-getY() ) endl;point-setPoint(3, 4);cout point: ( point-getX() , point-getY() ) endl;// 指针使用完成后需要将指针指到起始地址point - 2;// 释放内存delete point;point nullptr;return 0;
}
运行结果
在 linux 环境直接运行报错但在 Windows 环境下可以正确运行这就造成了内存泄漏。 对象成员 对象成员即对象中包含其他的对象。 这里示例代码将继续使用示例代码 1 中的点类。
示例代码 7
首先看一下当对象 A 有对象 B 时调用构造函数与析构函数的顺序。
class Line
{
public:Line(const Point pA, const Point pB) : pointA(pA), pointB(pB) {cout Line(const Point pA, const Point pB) endl;}Line(double aX, double aY, double bX, double bY) : pointA(aX, aY), pointB(bX, bY) {cout Line(double aX, double aY, double bX, double bY) endl;}~Line() {cout ~Line() endl;}
private:Point pointA;Point pointB;
};int main()
{// 实例化Line *line new Line(1, 2, 3, 5);// 释放内存delete line;line nullptr;return 0;
}
运行结果可以看到先调用 pointA 的构造函数再调用 pointB 的构造函数最后调用 Line 的构造函数而析构函数时正好反过来的。这也是为什么当对象成员没有默认构造函数时必须要使用初始化列表的原因因为对象成员先于对象初始化。 示例代码 8 如果将对象成员类型作为参数输入时看看其调用构造函数以及析构函数的顺序。 int main()
{// 实例化Line *line new Line(Point(1, 2), Point(3, 5));// 释放内存delete line;line nullptr;return 0;
}
运行结果对象成员类型作为参数传入时传入的参数时会临时创建两个对象初始化完成后临时对象自动销毁。 示例代码 9
int main()
{Line *p new Line(1, 2, 3, 4);cout sizeof (p) sizeof (p) endl;cout sizeof (Line) sizeof (Line) endl;delete p;p nullptr;return 0;
}
运行结果p 指针占 8 字节Line 类中有两个 Point 类数据成员Point 类有两个 double 类型数据成员所以 Line 一共占 32 个字节。 实验总结
使用对象数组时会调用每个对象的构造函数和析构函数。new 与 deletenew [] 与 delete [] 一定要配套使用。不要越界不管是栈还是堆访问数组时都不要越界。对象数组指针变量本身就是一个指针。堆上实例化的数组要注意指针使用方法。如果是做项目要考虑使用在堆上实例化申请内存栈空间比堆空间小很多。当对象 A 中有常量时必须使用初始化列表。当对象 A 有其他的对象 B 并且对象 B 没有默认构造参数时需要使用初始化列表。除了以上两种情况可以不使用初始化列表但是推荐使用初始化列表。对象数据成员和对象成员先于对象初始化。在实例化对象时需要清楚初始化数据成员的顺序。