C++/CLI——2类和对象生存期

news2024/11/28 18:38:58

C++/CLI——2函数与类的使用方法

函数使用

定义函数和使用函数基本与C#相同,只不过C++/CLI可以像标准C++一样,可以先声明函数原型,再定义函数主体。值得注意的是,如果有默认参数,只能在函数原型中定义,不能在函数主体中重复定义。

#include "pch.h"
using namespace System;

//函数原型
double GetResult(double a=1, double b=1);

//函数主体
double GetResult(double a, double b)
{
	return a + b;
}
//调用函数
int main(array<System::String^>^ args)
{
	double r = GetResult(100);
	Console::WriteLine(r);
	return 0;
}

C++/CLI中的语句如if、switch、while、for、do while等,均与C#用法相同。

类和对象

定义类

和标准C++一样,在.h头文件中声明原型,在.cpp源文件中定义实现。使用::表示C++作用域解析符,->表示调用成员操作符。

案例:

//头文件
#pragma once
ref class CreditCardAccount
{
public:
	CreditCardAccount();
	CreditCardAccount(long number, double balance, double limit);
	static CreditCardAccount();//静态类构造器
	void SetCreditLimit(double amount);
	bool MakePurchase(double amount);
	void MakeRepayment(double amount);
	void PrintStatement();
	long GetAccountNumber();
	static int GetNumberOfAccounts();
private:
	long accountNumber;
	double currentBalance;
	double creditLimit;
	static int NumberOfAccounts;
};

源文件

#include "pch.h"
#include "CreditCardAccount.h"
using namespace System;

CreditCardAccount::CreditCardAccount()
{
}
静态类构造器
static CreditCardAccount::CreditCardAccount()
{
}
//成员初始化列表
CreditCardAccount::CreditCardAccount(long number, double balance, double limit):accountNumber(number), currentBalance(balance), creditLimit(limit)
{
}
void CreditCardAccount::SetCreditLimit(double amount)
{
	creditLimit = amount;
}
bool CreditCardAccount::MakePurchase(double amount)
{
	if (currentBalance +amount>creditLimit)
	{
		return false;
	}
	else
	{
		currentBalance += amount;
		return true;
	}
}

void CreditCardAccount::MakeRepayment(double amount)
{
	currentBalance -= amount;
}

void CreditCardAccount::PrintStatement()
{
	Console::WriteLine(currentBalance);
}

long CreditCardAccount::GetAccountNumber()
{
	return accountNumber;
}
//此时没有static,只有在函数原型时含有static
int CreditCardAccount::GetNumberOfAccounts()
{
	return 0;
}

调用

#include "pch.h"
#include "CreditCardAccount.h"
using namespace System;

int main(array<System::String^>^ args)
{
	CreditCardAccount^ myAccount = gcnew CreditCardAccount();
	myAccount->SetCreditLimit(1000);
	myAccount->MakePurchase(1000);
	myAccount->PrintStatement();
	long num = myAccount->GetAccountNumber();
	Console::WriteLine(num);
}

类构造器

不同于标准C++,C++/CLI支持静态构造器,普通构造器是在对象创建时初始化实例成员,而静态构造器是在类可以使用前完成一些准备工作,这意味着在创建类的任何对象,或者在使用类的任何静态成员之前都会先调用类静态构造函数。

//h文件 
ref class CreditCardAccount
{
public:
	static CreditCardAccount();
};
//cpp文件
static CreditCardAccount::CreditCardAccount()
{

}

类级常量

类级常量代表在类的所有实例中都不变的值,可以使用literal关键字来创建

literal string^ name ="hello";

在标准的C++中可以使用static const来表示类级别常量,虽然C++/CLI也支持这样写,但是如果类时通过一个#using语句来访问,这种常量不被认为是编译时常量,所以推荐使用literal

实例常量

可以用initonly关键字来标记实例常量,也就是在创建类的实例时由构造器赋值,之后就不能更改。

对象生存期

C++/CLI中垃圾回收器(GC)来清理不需要的对象,垃圾回收器有三点要注意:

  1. 对象总是通过句柄来管理,这是系统跟踪对象的方式
  2. 只要有一个句柄指向对象,该对象就不会被回收
  3. 无法判断对象的内存在什么时候回收,这有GC来决定

垃圾回收器有一个原则越老的对象存活时间越长,因为gc有“代”的概念,0代满了就对0代进行回收,幸存下来的对象提升到1代,目前gc只支持3代。

析构器

标准C++和C#都具有析构器,只不过C#中的析构器不能显式调用,C++/CLI定义方式与之相同,使用方法和标准C++类似,使用delete来调用析构器

ref class MyClass
{
public:
	MyClass();
	~MyClass();

};

int main(array<System::String^>^ args)
{
	MyClass^ c = gcnew MyClass();
	delete c;
}

终结器

垃圾回收器回收对象时调用的是终结器,如果使用了非托管资源,则要定义终结器,如果未使用非托管资源,则一般不需要定义终结器,与C#中的析构器类似,不能显式调用。

ref class MyClass
{
public:
	MyClass();
	!MyClass();
};

终结期的三个原则:

  1. 不要定义什么都不做的终结器,因为一旦定义了,GC就会在回收对象的内存中执行,这会有性能损耗
  2. 终结期的顺序不能确定,如A和B都有终结器,且都对同一数据进行操作,这是无法确定谁先执行的
  3. 如果整个应用程序已经终止,仍然存活的对象不会再调用终结器,这包括后台线程使用的对象,或者在终结器中创建的对象。
案例
ref class MyClass
{
public:
	MyClass(String^ name);
	~MyClass();
	!MyClass();
	void DoSomething();

private:
	String^ name;
};


MyClass::MyClass(String^ name)
{
	this->name = name;
	Console::WriteLine("构造函数已调用:{0}", name);
}

MyClass::~MyClass()
{
	Console::WriteLine("析构函数调用");
}

MyClass::!MyClass()
{
	Console::WriteLine("终结期调用");
}

void MyClass::DoSomething()
{
	Console::WriteLine("调用方法");
}

int main(array<System::String^>^ args)
{
	MyClass^ m1 = gcnew MyClass("hello");
	m1->DoSomething();
	Console::WriteLine("程序结束");
}

image-20231229113106011

修改案例

如果在调用时,显式调用delete

int main(array<System::String^>^ args)
{
	MyClass^ m1 = gcnew MyClass("hello");
	m1->DoSomething();
	delete m1;
	Console::WriteLine("程序结束");
}

image-20231229113243075

此时程序没有调用终结器,因为垃圾回收器认为析构器执行完成后,对象已经得到清理,不需要执行终结器,所以一般要在析构器中显式调用终结器。

MyClass::~MyClass()
{
	Console::WriteLine("析构函数调用");
	this->!MyClass();
}

在编写C++/cli程序时,要养成使用对象完毕后调用delete的习惯

栈语义

标准C++中,可以在栈上直接创建对象

MyClass m("dd");
m.DoSomething();

这样在离开作用域后会自动销毁,因为是在栈上定义的对象,所以说对象具有栈的语义

C++/CLI支持相同的方式,不过,实际上并不是在栈上声明,只是为了兼容C++的写法,目前大多数类型对象都支持栈语义,除了字符串和数组,这些对象必须使用gcnew来获得。

拷贝构造器

标准C++的内存管理严重依赖拷贝构造器,如果没有定义则默认提供一个拷贝构造器,但是C++/CLI有了GC,并不会默认提供一个拷贝构造函数,而是需要自己写。

MyClass^ a = gcnew MyClass();
MyClass^ b =a;

如何像上面这样写,则b和a其实指向的是同一个对象,拷贝的只是句柄而不是拷贝指向的对象。如果要完全拷贝,则要提供拷贝构造器。

ref class MyClass
{
public:
	MyClass(const MyClass% other);

private:
	String^ name;
	int value;
};

MyClass::MyClass(const MyClass% other)
{
	value = other.value;
	name = other.name;
}

%指定了一个跟踪引用,句柄间接引用对象,使用->操作符访问成员,而跟踪引用只是变量的别名,是变量的另一个名字。类似于标准C++中的引用,只不过为了应对GC操作,C++/CLI的引用为%

int i =5;
int %j = i;	

拷贝构造函数一般使用const来修饰参数,这样做的好处主要有2个:

  1. 引用不产生新的变量,减少形参与实参传递时的开销
  2. 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用

与标准C++一样,C++/Cli也提供了*解引用符

MyClass^ m = gcnew MyClass();
//rm其实和和m其实是指向了同一个对象
MyClass% rm = *m;
// mm是具有语义栈的对象,其实是调用了拷贝构造器
MyClass mm=*m;
案例
ref class MyClass
{
public:
	MyClass(const MyClass% other);
	MyClass(int v);
	int GetValue();
	Void SetValue(int v);

private:
	int value;
};

MyClass::MyClass(const MyClass% other)
{
	value = other.value;
}

MyClass::MyClass(int v)
{
	value = v;
}

int MyClass::GetValue()
{
	return value;
}

Void MyClass::SetValue(int v)
{
	value = v;
}

int main(array<System::String^>^ args)
{
	MyClass^ a = gcnew MyClass(1);
	MyClass^ b = a;//仅仅拷贝句柄
	b->SetValue(10);
	Console::WriteLine("a的值为{0}", a->GetValue());
	Console::WriteLine("b的值为{0}", b->GetValue());

	MyClass% rb = *a;//只是用了跟踪引用
	rb.SetValue(20);
	Console::WriteLine("a的值为{0}", a->GetValue());
	Console::WriteLine("rb的值为{0}", rb.GetValue());

	MyClass c = *a;//创建新对象,并把它作为a所指对象的拷贝
	c.SetValue(100);
	Console::WriteLine("a的值为{0}", a->GetValue());
	Console::WriteLine("c的值为{0}", c.GetValue());

	MyClass^ d = gcnew MyClass(*a);//直接调用拷贝构造器
	d->SetValue(1000);
	Console::WriteLine("a的值为{0}", a->GetValue());
	Console::WriteLine("d的值为{0}", d->GetValue());

	Console::WriteLine("程序结束");
}

image-20231229142936889

对象和栈语义关联

对象经常由其他对象构成,包含对象可以使用栈的语义来声明,也可以使用句柄来声明。在使用栈的语义来声明时,包含对象是在调用构造函数之前构造的,而析构器正好相反。那如何选择是使用栈的语义声明还是句柄来声明,要处理以下几个问题:

  1. 包含对象是否是容器对象的一部分,是否能独立存在;
  2. 包含对象是否要与其他对象共享;
  3. 包含对象是否可以与其他对象交换;
  4. 包含对象是否在容器对象销毁后可以继续存活;

如果这几个都不满足,则优先使用栈的语义声明方式。

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

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

相关文章

SpringBoot 请求参数

文章目录 一、简单参数实体参数数组集合参数日期参数Json参数路径参数 一、简单参数 原始方式 在原始的web程序中&#xff0c;获取请求参数&#xff0c;需要通过HttpServletRequest 对象手动获取。 SpringBoot方式 1.参数名与形参变量名相同&#xff0c;定义形参即可接收参数。…

紫光展锐5G扬帆出海 | 东南亚成为5G新热土

东南亚是一块充满活力和潜力的市场&#xff0c;这里人口基数大、年轻消费群体占比高&#xff0c;电子市场在过去几年显著增长。 增速“狂飙”的东南亚手游 近年来&#xff0c;东南亚手游下载量逐年增长&#xff0c;2023 年第一季度下载量突破 21 亿次&#xff0c;贡献了全球近…

QT上位机开发(带配置文件的倒计时软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们用qt写过倒计时软件&#xff0c;但是那个时候界面只有分钟和秒钟&#xff0c;这一次我们希望在之前的基础上拓展一下。第一&#xff0c;可…

【数据结构——图】图的最短路径(头歌习题)【合集】

目录 第1关&#xff1a;单源最短路径完整代码 第2关&#xff1a;多源最短路径输入格式:输出格式:完整代码 第1关&#xff1a;单源最短路径 给一个n(1 ≤ n ≤ 2500) 个点 m(1 ≤ m ≤ 6200) 条边的无向图&#xff0c;求 s 到 t 的最短路。 输入格式: 第一行四个由空格隔开的整…

二叉堆的简单板子+理解+例题

首先&#xff0c;我们先要了解堆是什么&#xff1f; 堆&#xff1a;是一种高级树状数据结构&#xff0c;是一种完全二叉树。 &#xff08;完全二叉树指的是&#xff0c;除了叶子节点&#xff0c;每个节点均有左右两个子节点的树状结构&#xff09; 而&#xff0c;二叉堆是堆的最…

爱吃饼干的小白鼠2023年终总结

目录 前言 学习生活经历 回顾2023 参加活动 回顾点点滴滴 展望2024 2024年新的起点和目标 前言 大家好&#xff0c;我是爱吃饼干的小白鼠。今天刚好是2024年1月1日&#xff0c;时间飞逝&#xff0c;2023年过的飞快&#xff0c;我已经入驻CSDN有一年了&#xff0c;这一年…

计算机组成原理——中央处理器cpu21-40

18、某计算机的指令流水线由4个功能段组成&#xff0c;指令流经各功能段的时间&#xff08;忽略各功能段之间的缓存时间&#xff09;分别为90ns、80ns、70ns和60ns&#xff0c;则该计算机的CPU时钟周期至少是多少。A A、 90ns     B、 80ns C、 70ns     D、 60ns …

dll文件和exe文件的区别和关系

dll文件 DLL(Dynamic Link Library)文件为动态链接库文件&#xff0c;又称"应用程序拓展"&#xff0c;是软件文件类型。在Windows中&#xff0c;许多应用程序并不是一个完整的可执行文件&#xff0c;它们被分割成一些相对独立的动态链接库&#xff0c;即DLL文件&…

把类成员函数作为参数传递给thread类......

(1)把类成员函数作为参数传递给thread类 一般地&#xff0c;在调用类的非静态函数时&#xff0c;编译器会隐式添加一参数&#xff0c;它是所操作对象的地址&#xff0c; 用于绑定对象和成员函数&#xff0c;并且位于所有其他实际参数之前。例如&#xff0c;类example具有成员函…

CCNP课程实验-Route_Path_Control_CFG

目录 实验条件网络拓朴需求 基础配置需求实现1.A---F所有区用Loopback模拟&#xff0c;地址格式为&#xff1a;XX.XX.XX.XX/32&#xff0c;其中X为路由器编号。根据拓扑宣告进对应协议。A1和A2区为特例&#xff0c;A1&#xff1a;55.55.55.0/24&#xff0c;A2&#xff1a;55.55…

Linux驱动学习—设备树及设备树下的platform总线

1、什么是设备树&#xff1f; 设备树是一种描述硬件资源的数据结构。他通过bootloader将硬件资源传给内核&#xff0c;使得内核和硬件资源 描述相对独立。 2、设备树的由来 2.1 平台总线的由来 要想了解为什么会有设备树&#xff0c;设备树是怎么来的&#xff0c;我们就要先…

71内网安全-域横向网络传输应用层隧道技术

必备知识点&#xff1b; 代理和隧道技术的区别&#xff1f; 代理主要解决的是网络访问问题&#xff0c;隧道是对过滤的绕过&#xff0c; 隧道技术是为了解决什么 解决被防火墙一些设备&#xff0c;ids&#xff08;入侵检测系统&#xff09;进行拦截的东西进行突破&#xff0…

了解.NET 通用主机

写在前面 .NET 通用主机负责应用启动和生存期管理&#xff0c;主机是封装应用资源和生存期功能的对象&#xff0c;通用主机可用于其他类型的 .NET 应用程序&#xff0c;如控制台应用&#xff1b;.NET 通用主机基于类库Microsoft.Extensions.Hosting 来实现&#xff0c;本文记录…

保护Word或Excel的几种方法,总有一种满足你的需求

你已经在Microsoft Word或Excel中创建了一个重要或机密文件,你希望将其保密或至少保持安全。也许你想确保只有你和某些人可以阅读或编辑它。也许你想限制某人可以对文件进行的修改类型。你甚至可以向读者保证这是最终版本。如果你知道在Word和Excel中使用哪些工具以及它们是如…

安装Node修改Node镜像地址搭建Vue脚手架创建Vue项目

1、安装VSCode和Node 下载VSCode Visual Studio Code - Code Editing. Redefined 下载Node Node.js (nodejs.org) 检验是否安装成功&#xff0c;WinR,输入cmd命令&#xff0c;使用node -v可以查看到其版本号 2、修改镜像地址 安装好node之后&#xff0c;开始修改镜像地址 …

UntiyShader(五)属性、内置文件和变量

目录 一、如何使用属性 例子 ShaderLab中的属性的类型和Cg中的变量的类型之间的匹配关系 二、Unity提供的内置文件和变量 内置的包含文件 内置的变量 一、如何使用属性 在一开始我们提到过&#xff0c;材质和UnityShader之间有着密切的练习&#xff0c;我们可以通过材质面…

前后台分离开发

前后台分离开发 简介 前后台分离开发&#xff0c;就是在项目开发过程中&#xff0c;对于前端代码的开发由专门的前端开发人员负责&#xff0c;后端代码则由后端开发人员负责&#xff0c;这样可以做到分工明确、各司其职&#xff0c;提高开发效率&#xff0c;前后端代码并行开…

OpenOCD简介和下载安装(Ubuntu)

文章目录 OpenOCD简介OpenOCD软件模块OpenOCD源码下载OpenOCD安装 OpenOCD简介 OpenOCD&#xff08;Open On-Chip Debugger&#xff09;开放式片上调试器 OpenOCD官网 https://openocd.org/&#xff0c;进入官网点击 About 可以看到OpenOCD最初的设计是由国外一个叫Dominic Ra…

Qt高质量的开源项目合集

文章目录 1.Qt官网下载/文档2.第三方开源 1.Qt官网下载/文档 Qt Downloads Qt 清华大学开源软件镜像站 Qt 官方博客 2.第三方开源 记录了平常项目开发中用到的第三方库&#xff0c;以及一些值得参考的项目&#xff01; Qt AV 基于Qt和FFmpeg的跨平台高性能音视频播放框…

这本书没有一个公式,却讲透了数学的本质

这本书没有一个公式&#xff0c;却讲透了数学的本质&#xff01; 《数学的雨伞下&#xff1a;理解世界的乐趣》。一本足以刷新观念的好书&#xff0c;从超市到对数再到相对论&#xff0c;娓娓道来。对于思维空间也给出一个更容易理解的角度。 作者&#xff1a;米卡埃尔•洛奈 …