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

news2024/12/23 19:03:53

构造函数与析构函数

例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++版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/481979.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python基础合集 练习21 (错误与异常处理语句)

‘’‘try: block1 except[ExceptionName]: block2 ‘’’ block1:执行代码,表示可能会出现错误的代码块 ExceptionName: 表示要捕获的异常名称,为可选参数.如果不指定异常名称,则表示捕获所有异常 block2:表示发生异常时执行的代码块 while True: try: num int(input(请输…

测试时,可快速调用 Mapper 的 Mapper Generator

项目 Gitee 地址&#xff1a;MapperGenerator (当前使用的是 JDK17&#xff0c;JDK8 的需改下 pom.xml 文件&#xff09; 解决的问题&#xff1a;SpringBootTest 启动太慢 使用方式 假设有这样一个数据库&#xff0c;名为 a SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;…

推荐 3 个令你惊艳的 GitHub 项目

昨日 GitHub Trending 上榜的开源项目&#xff0c;基于 AI 技术提高你的生产力。借助 AI 你能搭建自己的数字人、搭建自己的法律助手、文档分析助手。 本期推荐开源项目目录&#xff1a; 1. 数字人开源项目 2. AI 法律助手 3. 为 PDF 文档打招一个聊天机器人 01 数字人开源项目…

作业区域工服穿戴识别算法 yolov7

作业区域工服穿戴识别系统基于yolov7视频智能图像识别技术&#xff0c;作业区域工服穿戴识别算法模型利用深度学习技术&#xff0c;不需人为干预自动识别现场施工作业人员未按要求穿工作服行为&#xff0c;代替后台工作人员执勤时的人眼判断。YOLOv7 研究团队提出了基于 ELAN 的…

win10 全屏秒退

问题 程序比较老&#xff0c;而电脑配置很新窗口化无任何问题&#xff0c;但是一旦全屏就退出 解决方案 猜测可能是兼容性的问题。 定位发现&#xff1a;禁用全屏优化。 方式如下&#xff1a;右键配置。选择兼容性。 选择禁用全屏优化。以兼容性运行这个程序。

什么是图数据库Neo4j

什么是图数据库Neo4j 所谓的图数据库一般由节点和关系构成&#xff0c;neo4j是其中的一种 在寻求数据的关联性中优于传统数据库mysql 且neo4j支持上亿级别的节点和关系 传统图运算一般在内存中进行&#xff0c;无法处理整个知识图谱&#xff0c;neo4j可以在磁盘中完成图运算…

【官网解读】主页解读

1.简况 Quick Prototyping&#xff08;快速原型设计&#xff09; Build machine learning solutions on raw data&#xff08;原始数据&#xff09; in a few lines of code. State-of-the-art Techniques&#xff08;最先进的技术&#xff09; Automatically utilize SOTA…

如何进行物联网渗透测试?

渗透测试揭示了未知的安全漏洞&#xff0c;因为值得信赖的专业人员模拟威胁性攻击。他们深入挖掘固件和硬件&#xff0c;以查找漏洞和可访问性疏忽。 物联网(IoT)连接设备是严重且可预防的安全漏洞的意外来源&#xff0c;现在是时候像其他硬件一样对其进行渗透测试处理了。为什…

疑难问题定位案例复盘(二)

今天我们继续分析一个因野指针访问导致的内存异常、出现coredump问题。在上一篇案例中&#xff0c;我们分享了一个在内存被释放后&#xff0c;业务模块仍然在使用导致业务模块自身出现coredump的现象。其实&#xff0c;在使用野指针访问内存时还有一种可能&#xff0c;就是业务…

存储资源调优技术——SmartVirtualization异构虚拟化技术

目录 基本概念 相关专业术语 eDevLUN与外部LUN的关系 对异构存储系统接管的方式 基本概念 异构虚拟化技术&#xff0c;仅对块业务生效 当本端存储系统与异构存储系统相互连接后&#xff1b;本端存储系统能够将异构存储系统提供的存储资源当作本地存储资源进行使用并对其进行集中…

模型参数量(Parameters)和计算量(FLOPs)获取【使用thop】

Tips: 针对部分开源代码没有提供相关计算网络参数量和计算量的代码。这里给出一个通用的获取网络的参数量和计算量的方法。 使用thop即可快速获取 1 模型参数量和计算量 参数量#params 即为网络模型中含有多少个参数&#xff0c;与输入的数据无关&#xff0c;主要与模型的结构…

在 windows 端使用 vscode + ubuntu WSL 优雅的使用原生 linux 的 gcc/g++ 编译

当我在windows上进行c的开发时&#xff0c;通常会使用 vs 但缺点也比较明显&#xff0c;他不原生的 gcc 编译器&#xff0c;其次 vs 这个 IDE 太过于庞大&#xff0c;当然也有很多人会使用 vscode 但是&#xff0c;在 windows 中安装 gcc/g 的过程极为复杂&#xff0c;且因为我…

【C++】右值引用完美转发

文章目录 右值引用和左值引用左值和右值概念左值引用 && 右值引用右值引用使用场景和意义左值引用的使用场景**左值引用的短板:**右值引用和移动语义STL容器增加的接口move函数右值引用的其他使用场景 完美转发万能引用完美转发保持值的属性完美转发的使用场景 右值引用…

Python基础合集 练习16(函数的递归)

简单的递归 1.求阶乘 比如输入为3 那么3216 输出就为6 def fun(n): if n1: return 1 return n*fun(n-1) numint(input(‘输入数字&#xff1a;’)) print(‘该数的阶乘为:’,fun(num)) 2.数列 这个数从第三项开始每一项为前一项的两倍 假如输入为6 则[1,1,2,3,5,8] 第六位为…

Python 时间处理模块

import time # 导入时间模块 s_time time.time() # 程序开始的时间 time.sleep(3) # 慢三秒 print(f’cost{time.time()-s_time}’) # 用当前时间减去之前程序开始的时间 print(time.localtime()) # 打印当前的时间 时间对象 print(time.gmtime()) # 打印当前时间&#xff0…

Python基础合集 练习18(类与对象2)

访问限制 class Dog_dog(): def init(self, name) -> None: self.__name name # 定义私有属性 def run(self):print(我跑的很快!我的名字是, self.__name)dog1 Dog_dog(‘papi’) dog1.run() print(dog1._Dog_dog__name) # 加入类名访问 print(’-----分界线-----’) p…

Mix.AI.How is your Chirper?

Chirper.ai是什么&#xff1f; 一个名为Chirper的AI驱动的社交网络平台。Chirper为AI用户建立了一个独立的环境&#xff0c;与传统的人类社交媒体平台有所区别。是一个独特的交互和协作空间&#xff0c;只有AI可以进行交流和互动。 shadow 看看我的AI化身每天都在干什么&#x…

Python---多线程编程、基于Socket完成服务端程序开发、基于Socket完成客户端程序开发

1. 进程&#xff1a; 程序在操作系统内运行&#xff0c;即成为一个运行进程 线程&#xff1a; 进程内部可以有多个线程&#xff0c;程序的运行本质上就是由进程内部的线程在实际工作的。 并行执行&#xff1a; 多个进程同时在运行&#xff0c;即不同的程序同时运行&#xff…

【Floyd】CF1204 Anna, Svyatoslav and Maps

看错题意了一直看不懂题解浪费了好多时间... 思路&#xff1a; 先去考虑特殊的地方&#xff1a; 100 子序列 最短路径 一个个剖析&#xff1a; 100&#xff1a;考虑Floyd 子序列&#xff1a;DP?不懂 最短路径&#xff1a;考虑Floyd 我们要求的是子序列的方案&#xff0…

【MySQL 8.0】搭建一个使用 SSL 加密的 MySQL 主从复制拓扑(基于 Binlog 行复制)

文章目录 先决条件搭建配置源、副本服务器选项文件 验证启用了 SSL 连接创建复制用户获取复制源服务器的二进制日志坐标配置复制源开启复制 验证进阶 之前&#xff0c;我们一起搭建了一个极简 MySQL 主从复制拓扑&#xff08;基于 Binlog 行复制&#xff09;&#xff0c;这是一…