【计算机图形学基础教程】面向对象程序设计基础

构造函数与析构函数

例1

设计一个长方形CRectangle类,调用类的成员函数计算长方形的周长和面积。

#include <iostream>

class CRectangle
{
public:
	CRectangle();                       // 声明默认构造函数
	CRectangle(int width, int height);  // 声明带参构造函数
	~CRectangle();                      // 声明析构函数 
	double perimeter();                   // 声明计算周长成员函数
	double area();                      // 声明计算面积成员函数
private:
	int width;                          // 声明长方形的宽度成员变量
	int height;                         // 声明长方形的高度成员变量
};

CRectangle::CRectangle()                                // 定义默认构造函数
{
	width = 10;
	height = 5;
	std::cout << "Created CRectangle!" << std::endl;
}
 
CRectangle::CRectangle(int width, int height)           // 定义带参构造函数
{
	this->width = width;
	this->height = height;
	std::cout << "Created CRectangle!" << std::endl;
}

CRectangle::~CRectangle()                                // 定义析构函数
{
	std::cout << "Destroyed CRectangle!" << std::endl;   
}

double CRectangle::perimeter()                           // 定义计算周长成员函数
{
	return 2 * (width + height);
}

double CRectangle::area()                                // 定义计算面积成员函数
{
	return width * height;
}

int main()
{
	CRectangle Rect1, Rect2(30, 20), *pRect = &Rect2;   // 定义对象和指向对象的指针
	// 使用对象输出周长和面积
	std::cout << "perimeter of Rect1: " << Rect1.perimeter() << std::endl;   // 使用对象输出Rect1的周长
	std::cout << "area of Rect1: " << Rect1.area() << std::endl;   // 使用对象输出Rect1的面积
	std::cout << "perimeter of Rect2: " << Rect2.perimeter() << std::endl;   // 使用对象输出Rect2的周长
	std::cout << "area of Rect1: " << Rect1.area() << std::endl;   // 使用对象输出Rect2的面积
	// 使用对象指针输出Rect2的周长和面积
	std::cout << "perimeter of Rect2: " << pRect->perimeter() << std::endl;   // 使用指向对象的指针输出Rect2的周长
	std::cout << "area of Rect2: " << pRect->area() << std::endl;   // 使用指向对象的指针输出Rect2的面积
}

对象的动态建立与释放

C++内存空间通常4个分区

  • 全局数据区(data area):全局变量、静态数据、常量
  • 代码区(code area):所有类成员函数和非成员函数代码
  • 栈区(stack area):为运行而分配的局部变量、函数参数、返回数据、返回地址等
  • 堆区(heap area),也称自由存储区:余下的空间

动态存储分配

有些操作对象的值只有在程序运行时才能确定,编译器无法在编译时预留存储空间,只能运行时进行内存分配,这种分配方式为动态存储分配。动态存储分配在堆区进行。

当需要一个动态分配的对象,必须向系统申请堆中的存储空间,并且当声明周期结束时,要显式释放所占用的内存空间。

使用new和delete可以根据需要动态建立和撤销对象。

建立对象格式:类名 * 指针变量名 = new 类型(初始化式)
释放对象格式:delete 指向该对象的指针变量名

使用new创建的对象式无名对象,不能使用对象名访问,只能使用指针访问。

一维对象数组动态内存的分配与释放

一维对象数组内存分配格式:类名 * 指针变量名 = new 类名[下标表达式]
一维对象数组的内存释放格式:delete [] 指向该数组的指针变量名

二维对象数组动态内存的分配与释放

二维数组是一种特殊的一维数组,其元素又是一维数组

在这里插入图片描述

  • R是二维数组的首地址,行指针;
  • R[0]和R[1]是第0列元素的地址,成为列指针;

二维对象数组的内存分配格式:

类名 **指针变量名 = new 类名 * [行下标表达式];
for (int i = 0; i < 行下标表达式; i++)
{
   指针标量名[i] = new 类名[列下标表达式];
}

二维对象数组的内存释放格式:

for(int i = 0; i < 行下标表达式; i++)
{
    delete [] 指针标量名[i];
}
delete [] 指针变量名;

由堆区创建的对象数组,只能调用默认的构造函数,不能调用其他任何构造函数。
二维动态数组在计算机图形学中一般用于建立三维物体的顶点表和面表。

例2

动态创建CRectangle类二维对象数组,输出每个对象的周长和面积。

#include <iostream>

class CRectangle
{
public:
	CRectangle();                       // 声明默认构造函数
	CRectangle(int width, int height);  // 声明带参构造函数
	~CRectangle();                      // 声明析构函数 
	double perimeter();                   // 声明计算周长成员函数
	double area();                      // 声明计算面积成员函数
private:
	int width;                          // 声明长方形的宽度成员变量
	int height;                         // 声明长方形的高度成员变量
};

CRectangle::CRectangle()                                // 定义默认构造函数
{
	width = 10;
	height = 5;
	std::cout << "Created CRectangle!" << std::endl;
}

CRectangle::CRectangle(int width, int height)           // 定义带参构造函数
{
	this->width = width;
	this->height = height;
	std::cout << "Created CRectangle!" << std::endl;
}

CRectangle::~CRectangle()                                // 定义析构函数
{
	std::cout << "Destroyed CRectangle!" << std::endl;
}

double CRectangle::perimeter()                           // 定义计算周长成员函数
{
	return 2 * (width + height);
}

double CRectangle::area()                                // 定义计算面积成员函数
{
	return width * height;
}

int main()
{

	int RowIndex = 2, ColIndex = 3;    // 设置二维数组行下标和列下标
	CRectangle **pRect;
	// 创建二维动态数组
	pRect = new CRectangle *[RowIndex];   // 设置行
	for (int i = 0; i < RowIndex; i++)
	{
		pRect[i] = new CRectangle[ColIndex];  // 设置列
	}

	for (int i = 0; i < RowIndex; i++)    // 设置周长和面积
	{
		for (int j = 0; j < ColIndex; j++)
		{
			std::cout << "Perimeter of Rect[" << i << "," << j << "] is " << pRect[i][j].perimeter() << std::endl;   // pRect[i][j]是对象,可以用.访问成员函数
			std::cout << "Area of Rect[" << i << "," << j << "] is " << pRect[i][j].area() << std::endl;
		}
	}

	// 释放二维动态数组
	for (int k = 0; k < RowIndex; k++)
	{
		delete[]  pRect[k];
		pRect[k] = NULL;
	}
	delete [] pRect;
	pRect = NULL;
	std::cin.get(); 
}

第一个循环创建pRect[0], 打印:
Created CRectangle!
Created CRectangle!
Created CRectangle!
说明pRect[i] = new CRectangle[ColIndex];会调用ColIndex次构造函数
第二个循环创建pRect[1],打印:
Created CRectangle!
Created CRectangle!
Created CRectangle!

随后,打印初始化后的周长和面积:
Perimeter of Rect[0,0] is 30
Area of Rect[0,0] is 50
Perimeter of Rect[0,1] is 30
Area of Rect[0,1] is 50
Perimeter of Rect[0,2] is 30
Area of Rect[0,2] is 50
Perimeter of Rect[1,0] is 30
Area of Rect[1,0] is 50
Perimeter of Rect[1,1] is 30
Area of Rect[1,1] is 50
Perimeter of Rect[1,2] is 30
Area of Rect[1,2] is 50

然后,将创建的动态对象数组释放掉,先释放pRect[0],调用3次析构函数,打印:
Destroyed CRectangle!
Destroyed CRectangle!
Destroyed CRectangle!

再释放pRect[1],打印三次:
Destroyed CRectangle!
Destroyed CRectangle!
Destroyed CRectangle!

最后,需要释放pRect,由于创建的对象CRectangle内存都已经释放掉了,所以不会调用再调用析构函数,但是pRect的内存仍存在,所以仍然需要显式delete。

动态链表

需要动态开辟一段内存空间来存储对象。在堆链表的结点进行访问时,通过存放在上一结点内的指向下一结点的指针,可以依次访问每个结点,构成链表关系。
在这里插入图片描述

例3

建立一个桶类,其中成员变量ScanLine为扫描线的位置,pNext为指向下一结点的指针。请动态建立两个桶结点构成的桶链表,并依次输出其扫描线的位置。

#include <iostream>

class CBucket     // 定义桶类
{
public:
	int ScanLine;     // 声明扫描线位置
	CBucket *pNext;   // 声明指向下一桶结点的指针
};

int main()
{
	CBucket *pHead = new CBucket, *pCurrentB;  // 定义桶结点的头指针和桶结点的当前指针
	pCurrentB = pHead;    // 当前指针为桶结点的头指针,开始给第一个结点赋值
	pCurrentB->ScanLine = 1;   // 当前指针(第一个结点)的扫描线为1
	std::cout << "The ScanLine of the first node is " << pCurrentB->ScanLine << std::endl;  // 输出当前指针的扫描线位置
	pCurrentB->pNext = new CBucket;    //为当前指针(第一个结点)的pNext指针新建结点
	pCurrentB = pCurrentB->pNext;   // 将当前指针指向新建结点
	pCurrentB->ScanLine = 2;     // 当前指针(第二个结点)的扫描线位置为2
	std::cout << "The ScanLine of the second node is " << pCurrentB->ScanLine << std::endl;  // 输出当前指针的扫描线位置
	pCurrentB->pNext = NULL;   // 只构造两个结点,当前指针(第二个结点)的pNecxt指针置空
	delete pHead;    // 释放头指针所指向的内存空间
	delete pCurrentB;  // 释放当前指针所指向的内存空间
	pHead = NULL;   // 头指针置空
	pCurrentB = NULL;  // 当前指针置空

	std::cin.get();
}

运行程序,打印:
The ScanLine of the first node is 1
The ScanLine of the second node is 2

在这里插入图片描述

继承与派生

继承是指在已有类的基础上增加新的内容创建一个新类。

派生类的定义

class 派生类名:[继承方式]基类名
{
    派生类新增加的成员;
};

继承方式:指派生类访问控制方式,用于控制基类中声明的成员在多大范围内能被派生类中的成员函数访问

继承方式有3种:

  • 公有继承public
  • 私有继承private
  • 保护继承protected

基类的构造函数是不能被继承的,对继承过来的基类成员的初始化工作要由派生类的构造函数完成。在设计派生类构造函数时,不仅要考虑对派生类新增加的成员变量初始化,还应当考虑对基类成员变量的初始化。
实现方法是执行派生类的构造函数时调用基类的构造函数。

派生类构造函数的定义:

派生类构造函数名(总参数表列):基类构造函数名(参数表列)
    {派生类中新增成员变量初始化语句}

例4

从已有的长方形类CRectangle继承出长方形类CCuboid,增加长度成员变量length和计算体积成员函数volumn(),试计算长方体的体积。

#include <iostream>

class CRectangle
{
public:
	CRectangle(int width, int height);  // 声明带参构造函数
	~CRectangle();                      // 声明析构函数 
	double perimeter();                   // 声明计算周长成员函数
	double area();                      // 声明计算面积成员函数
protected:
	int width;                          // 声明长方形的宽度成员变量
	int height;                         // 声明长方形的高度成员变量
};


CRectangle::CRectangle(int width, int height)           // 定义带参构造函数
{
	this->width = width;
	this->height = height;
	std::cout << "Created CRectangle!" << std::endl;
}

CRectangle::~CRectangle()                                // 定义析构函数
{
	std::cout << "Destroyed CRectangle!" << std::endl;
}

double CRectangle::perimeter()                           // 定义计算周长成员函数
{
	return 2 * (width + height);
}

double CRectangle::area()                                // 定义计算面积成员函数
{
	return width * height;
}

class CCuboid : public CRectangle               // 公有继承的派生类
{
public:
	CCuboid(int width, int height, int length);    // 声明派生类构造函数
	~CCuboid();                                    // 声明派生类析构函数
	double volume();                         //  声明派生类计算体积成员函数
private:
	int length;                              // 声明派生类长度成员变量

};

CCuboid::CCuboid(int width, int height, int length) : CRectangle(width, height)    // 定义派生类构造函数,width, height的初始化继承自基类
{
	this->length = length;
	std::cout << "Created CCuboid!" << std::endl;
}

CCuboid::~CCuboid()                                   // 定义派生类析构函数
{
	std::cout << "Destroyed CCuboid!" << std::endl;
}

double CCuboid::volume()                   // 定义派生类计算体积成员函数
{
	return width * height * length;
}

int main()
{
	CCuboid * pCuboid = new CCuboid(30, 20, 100);
	std::cout << "The volume of Cuboid is " << pCuboid->volume() << std::endl; 
	delete pCuboid;

	std::cin.get();
}

创建CCuboi指针pCuboid时,先调用基类的构造函数,再调用派生类的构造函数,打印:
Created CRectangle!
Created CCuboid!
然后,打印语句:
The volume of Cuboid is 60000
最后,释放pCuboid的内存,先调用派生类的析构函数,再调用基类的析构函数,打印:
Destroyed CCuboid!
Destroyed CRectangle!

在这里插入图片描述

参考:
孔令德, 计算机图形学基础教程(Visual C++版)

第1章绪论 1.1计算机图形学及其相关概念 1.2计算机图形学的发展 1.2.1计算机图形学学科的发展 1.2.2图形硬件设备的发展 1.2.3图形软件的发展 1.3计算机图形学的应用 1.3.1计算机辅助设计与制造 1.3.2计算机辅助绘图 1.3.3计算机辅助教学 1.3.4办公自动化和电子出版技术 1.3.5计算机艺术 1.3.6在工业控制及交通方面的应用 1.3.7在医疗卫生方面的应用 1.3.8图形用户界面 1.4计算机图形学研究动态 1.4.1计算机动画 1.4.2地理信息系统 1.4.3人机交互 1.4.4真实感图形显示 1.4.5虚拟现实 1.4.6科学计算可视化 1.4.7并行图形处理 第2章计算机图形系统及图形硬件 2.1计算机图形系统概述 2.1.1计算机图形系统的功能 2.1.2计算机图形系统的结构 2.2图形输入设备 2.2.1键盘 2.2.2鼠标器 2.2.3光笔 2.2.4触摸屏 2.2.5操纵杆 2.2.6跟踪球和空间球 2.2.7数据手套 2.2.8数字化仪 2.2.9图像扫描仪 2.2.10声频输入系统 2.2.11视频输入系统 2.3图形显示设备 2.3.1阴极射线管 2.3.2CRT图形显示器 2.3.3平板显示器 2.3.4三维观察设备 2.4图形显示子系统 2.4.1光栅扫描图形显示子系统的结构 2.4.2绘制流水线 2.4.3相关概念 2.5图形硬拷贝设备 2.5.1打印机 2.5.2绘图仪 2.6OpenGL图形软件包 2.6.1OpenGL的主要功能 2.6.2OpenGL的绘制流程 2.6.3OpenGL的基本语法 2.6.4一个完整的OpenGL程序 第3章用户接口与交互式技术 3.1用户接口设计 3.1.1用户模型 3.1.2显示屏幕的有效利用 3.1.3反馈 3.1.4一致性原则 3.1.5减少记忆量 3.1.6回退和出错处理 3.1.7联机帮助 3.1.8视觉效果设计 3.1.9适应不同的用户 3.2逻辑输入设备与输入处理 3.2.1逻辑输入设备 3.2.2输入模式 3.3交互式绘图技术 3.3.1基本交互式绘图技术 3.3.2三维交互技术 3.4OpenGL中橡皮筋技术的实现 3.4.1基于鼠标的实现 3.4.2基于键盘的实现 3.5OpenGL中拾取操作的实现 3.6OpenGL的菜单功能 第4章图形的表示与数据结构 4.1基本概念 4.1.1基本图形元素 4.1.2几何信息与拓扑信息 4.1.3坐标系 4.1.4实体的定义 4.1.5正则集合运算 4.1.6平面多面体与欧拉公式 4.2三维形体的表示 4.2.1多边形表面模型 4.2.2扫描表示 4.2.3构造实体几何法 4.2.4空间位置枚举表示 4.2.5八叉树 4.2.6BSP树 4.2.7OpenGL中的实体模型函数 4.3非规则对象的表示 4.3.1分形几何 4.3.2形状语法 4.3.3粒子系统 4.3.4基于物理的建模 4.3.5数据场的可视化 4.4层次建模 4.4.1段与层次建模 4.4.2层次模型的实现 4.4.3OpenGL中层次模型的实现 第5章基本图形生成算法 5.1直线的扫描转换 5.1.1数值微分法 5.1.2中点Bresenham算法 5.1.3Bresenham算法 5.2圆的扫描转换 5.2.1八分法画圆 5.2.2中点Bresenham画圆算法 5.3椭圆的扫描转换 5.3.1椭圆的特征 5.3.2椭圆的中点Bresenham算法 5.4多边形的扫描转换与区域填充 5.4.1多边形的扫描转换 5.4.2边缘填充算法 5.4.3区域填充 5.4.4其他相关概念 5.5字符处理 5.5.1点阵字符 5.5.2矢量字符 5.6属性处理 5.6.1线型和线宽 5.6.2字符的属性 5.6.3区域填充的属性 5.7反走样 5.7.1过取样 5.7.2简单的区域取样 5.7.3加权区域取样 5.8在OpenGL中绘制图形 5.8.1点的绘制 5.8.2直线的绘制 5.8.3多边形面的绘制 5.8.4OpenGL中的字符函数 5.8.5OpenGL中的反走样 第6章二维变换及二维观察 6.1基本概念 6.2基本几何变换 6.2.1平移变换 6.2.2比例变换 6.2.3旋转变换 6.2.4对称变换 6.2.5错切变换 6.2.6二维图形几何变换的计算 6.3复合变换 6.3.1二维复合平移变换和比例变换 6.3.2二维复合旋转变换 6.3.4其他二维复合变换 6.3.5相对任一参考点的二维几何变换 6.3.6相对于任意方向的二维几何变换 6.3.7坐标系之间的变换 6.3.8光栅变换 6.3.9变换的性质 6.4二维观察 6.4.1基本概念 6.4.2?用户坐标系到观察坐标系的变换 6.4.3?窗口到视区的变换 6.5?裁剪 6.5.1?点的裁剪 6.5.2直线段的裁剪 6.5.3多边形的裁剪 6.5.4其他裁剪 6.6OpenGL中的二维观察变换 第7章三维变换及三维观察 7.1三维变换的基本概念 7.1.1几何变换 7.1.2三维齐次坐标变换矩阵 7.1.3平面几何投影 7.2三维几何变换 7.2.1三维基本几何变换 7.2.2三维复合变换 7.3三维投影变换 7.3.1正投影 7.3.2斜投影 7.4透视投影 7.4.1一点透视 7.4.2二点透视 7.4.3三点透视 7.5观察坐标系及观察空间 7.5.1观察坐标系 7.5.2观察空间 7.6三维观察流程 7.6.1用户坐标系到观察坐标系的变换 7.6.2平行投影的规范化投影变换 7.6.3透视投影的规范化投影变换 7.7三维裁剪 7.7.1关于规范化观察空间的裁剪 7.7.2齐次坐标空间的裁剪 7.8OpenGL中的变换 7.8.1矩阵堆栈 7.8.2模型视图变换 7.8.3投影变换 7.8.4实例 第8章曲线与曲面 8.1基本概念 8.1.1曲线/曲面数学描述的发展 8.1.2曲线/曲面的表示要求 8.1.3曲线/曲面的表示 8.1.4插值与逼近 8.1.5连续性条件 8.1.6样条描述 8.2三次样条 8.2.1自然三次样条 8.2.2Hermite插值样条 8.3Bezier曲线/曲面 8.3.1Bezier曲线的定义 8.3.2Bezier曲线的性质 8.3.3Bezier曲线的生成 8.3.4Bezier曲面 8.4B样条曲线/曲面 8.4.1B样条曲线 8.4.2B样条曲线的性质 8.4.3B样条曲面 8.5有理样条曲线/曲面 8.5.1NURBS曲线/曲面的定义 8.5.2有理基函数的性质 8.5.3NURBS曲线/曲面的特点 8.6曲线/曲面的转换和计算 8.6.1样条曲线/曲面的转换 8.6.2样条曲线/曲面的离散生成 8.7OpenGL生成曲线/曲面 8.7.1Bezier曲线/曲面函数 8.7.2GLU中的B样条曲线/曲面函数 第9章消隐 9.1深度缓存器算法 9.2区间扫描线算法 9.3深度排序算法 9.4区域细分算法 9.5光线投射算法 9.6BSP树 9.7多边形区域排序算法 9.8OpenGL中的消隐处理 第10章真实感图形绘制 10.1简单光照模型 10.1.1环境光 10.1.2漫反射光 10.1.3镜面反射光 10.1.4光强衰减 10.1.5颜色 10.2基于简单光照模型的多边形绘制 10.2.1恒定光强的多边形绘制 10.2.2Gouraud明暗处理 10.2.3Phong明暗处理 10.3透明处理 10.4产生阴影 10.5模拟景物表面细节 10.5.1用多边形模拟表面细节 10.5.2纹理的定义和映射 10.5.3凹凸映射 10.6整体光照模型与光线追踪 10.6.1整体光照模型 10.6.2Whitted光照模型 10.6.3光线跟踪算法 10.6.4光线跟踪反走样 10.7 OpenGL中的光照与表面绘制函数 10.7.1 OpenGL点光源 10.7.2 OpenGL全局光照 10.7.3 OpenGL表面材质 10.7.4 OpenGL透明处理 10.7.5 OpenGL表面绘制 10.7.6 实例 10.8 OpenGL中的纹理映射
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值