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

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. 包含对象是否在容器对象销毁后可以继续存活;

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023,最后

最后一天确实来得飞快&#xff0c;今年也不算什么特殊的一年&#xff0c;所以总结似乎也显得没有特别重要&#xff0c;但是既然它来了&#xff0c;也就要走了&#xff0c;老子也顺道给他做一次结束的话。 ——我和我的技术交流群 启发和启发的朋友们是2023年我特别关注的一群人…

Yapi接口管理平台Centos7容器部署

文章目录 0.Docker部署1.Docker部署1.1 MongoDB1.2 下载 Yapi 镜像1.3 初始化数据库1.4 启动 Yapi 服务1.5 访问 Yapi 2.docker-compose部署2.1 创建容器网络2.2 创建2.3 创建 mongodb-compose2.4 创建 yapi-compose2.5 启动容器2.6 访问 Yapi 0.Docker部署 参考&#xff1a;C…

1.5 FMEA项目规划:5T

文章目录 1.5.1 FMEA目的1.5.2 FMEA时间节点1.5.3 FMEA团队1.5.3.1 设计FMEA团队1.5.3.2 过程FMEA团队1.5.3.3 FMEA团队角色和职责 1.5.4 FMEA任务1.5.5 FMEA工具 为确保及时获得最佳效果并避免FMEA返工&#xff0c;以下五个主题应在设计FMEA和过程FMEA开始时讨论&#xff0c;它…

labuladong日常刷题-差分数组 | LeetCode 1109航班预定统计 | 花式遍历 151反转字符串里的单词

差分数组–前缀和数组的升级 LeetCode 1109 航班预定统计 2024.1.1 题目链接labuladong讲解[链接] class Solution { public:vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {//构建航班人数数组&#xff0c;数组大小为n,初…

ICCV 2023 风格迁移方向 5 篇论文

1、StyleDiffusion: Controllable Disentangled Style Transfer via Diffusion Models 内容和风格&#xff08;Content and style disentanglement&#xff0c;C-S&#xff09;解耦是风格迁移的一个基本问题和关键挑战。基于显式定义&#xff08;例如Gram矩阵&#xff09;或隐式…

C++每日一练(9):观光缆车

题目描述 有n座两两相邻的山&#xff0c;山顶高度分别为a1&#xff0c;a2&#xff0c;...&#xff0c;an。蜗蜗想要在相邻的山顶间修建缆车&#xff0c;他想要知道相邻山峰之间最大的落差是多少&#xff1f; 输入 第一行一个整数n&#xff08;2<n<1000&#xff09;&#…

华为ensp网络设计期末测试题-复盘

网络拓扑图 地址分配表 vlan端口分配表 需求 The device is running!<Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]un in en Info: Information center is disabled. [Huawei]sys S1 [S1]vlan 99 [S1-vlan99]vlan 100 [S1-vlan100]des IT [S1-…

05 HAL库驱动蜂鸣器唱出一首小歌

目录 一、蜂鸣器的基本知识 1、有源蜂鸣器 2、无源蜂鸣器 二、PWM的相关知识 1. PWM概念 2. PWM常见参数 3.PWM基本结构 三、蜂鸣器发出音调的原理 四、频率计算 五、实验开始 一、蜂鸣器的基本知识 蜂鸣器是一种能够发出持续而连续的声音的电子设备&#xff0c;它被…

WINDOWS 批量修改图片文件名称(流星程序集之二十)

博主家里有一台电脑&#xff0c;存放家庭全部的照片和视频&#xff0c;从智能手机和3G网络发展开始&#xff0c;家里的照片和视频越来越多&#xff0c;已经达到上万个文件。终于&#xff0c;博主找到一个方法整理和保存这些珍贵的数据资料。 一、按年代目录整理照片和视频 按年…

低成本TB级数据库技术选型之思考两三点

一、背景 前段时间在搞毕业论文的选题&#xff0c;最头疼的就是大量的文献检索和阅读&#xff0c;从研究的角度上我们可以将文献分为四类&#xff1a; 理论文献&#xff1a;为研究提供理论的框架和基础的文献。这些文献可能并不会和所做的研究直接相关&#xff0c;甚至由于理…

「网络编程」其他重要的协议或技术_ DNS协议 | ICMP协议 | NAT技术

「前言」文章内容是DNS协议、ICMP协议、NAT技术的讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、DNS协议1.1 背景1.2 域名简介1.3 域名解析的过程 二、ICMP协议2.1 ICMP简介2.2 ping命令2.3 traceroute命令 三、NAT技术3.1 NAT技术背景3.2 …

elasticsearch系列五:集群的备份与恢复

概述 前几篇咱们讲了es的语法、存储的优化、常规运维等等&#xff0c;今天咱们看下如何备份数据和恢复数据。 在传统的关系型数据库中我们有多种备份方式&#xff0c;常见有热备、冷备、全量定时增量备份、通过开发程序备份等等&#xff0c;其实在es中是一样的。 官方建议采用s…

Python 基础语法01

变量声明 #运算 num 1 num 1 print("num 1",num)num - 1 print("num - 1", num)num * 4 print("num * 4",num)num 3 num % 2 print("num%2",num)num ** 2 print("num ** 2", num)num 9 num // 2 print("num // …

20231228在Firefly的AIO-3399J开发板的Android11的挖掘机的DTS配置单前置摄像头ov13850

20231228在Firefly的AIO-3399J开发板的Android11的挖掘机的DTS配置单前置摄像头ov13850 2023/12/28 10:42 【碰到一个很神奇的问题】&#xff1a; 昨天晚上前置摄像头怎么也点不亮&#xff01;改了巨多的地方&#xff01;晚上睡觉之前把开发板彻底断电了&#xff01;今天开电脑…

【JavaFX】JDK11 基于Gson、hutool、Jackson持久化存储实体类数据的解决方案 (读取、追加、去重、写入json对象)

文章目录 开发环境效果前言一、Gson是什么?二、使用步骤1.引入依赖2.创建实体类创建 JsonFileService类创建JsonFileService的实现类 JsonFileServiceImpl三、实现效果开发环境 JDK11IDEA 2023.3Gson、hutool、JacksonJavaFX 11效果 前言 使用JDK1

Easy Rules规则引擎实战

文章目录 简介pom 规则抽象规则Rule基础规则BasicRule事实类Facts&#xff1a;map条件接口动作接口 四种规则定义方式注解方式RuleBuilder 链式Mvel和Spel表达式Yml配置 常用规则类DefaultRuleSpELRule&#xff08;Spring的表达式注入&#xff09; 组合规则UnitRuleGroup 规则引…

在线智能防雷监控检测系统应用方案

在线智能防雷监控检测系统是一种利用现代信息技术&#xff0c;对防雷设施的运行状态进行实时监测、管理和控制的系统&#xff0c;它可以有效提高防雷保护的安全性、可靠性和智能化程度&#xff0c;降低运维成本和风险&#xff0c;为用户提供全方位的防雷解决方案。 地凯科技在…

爬取豆瓣电影评论内容、星级、评论时间、支持人数

大家好&#xff0c;我是带我去滑雪&#xff0c;每天教你一个小技巧&#xff01; 本期爬取豆瓣电影评论人、评论时间、星级、支持人数、评论内容。话不多说&#xff0c;直接上代码&#xff1a; import requests from bs4 import BeautifulSoup import pandas as pd import time…

轻松注册谷歌账号,获取谷歌邮箱(Gmail)

“ 国内手机号无法注册谷歌账户&#xff08;邮箱&#xff09;&#xff1f;很好解决呀&#xff01;” 经常遇到某些朋友需要一个谷歌邮箱&#xff0c;却总是卡在某些步骤。今天特地出个详细教程帮助大家轻松注册谷歌账号&#xff0c;获取谷歌邮箱&#xff08;Gmail&#xff09;。…

解密C++中的forward<int>(a)和forward<int >(a):你真的了解它们之间的区别吗?

一文看尽C中的forward完美转发 一、前言二、深入理解forward和完美转发三、对forward<int>(a)的解析四、对forward<int &&>(a)的解析五、forward<int>(a)和forward<int &&>(a)的区别总结 一、前言 完美转发在C中具有重要性&#xff0…