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

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

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

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

相关文章

桉木芯建筑模板与其他材质比较有何不同?

在建筑行业中&#xff0c;模板的选择对于确保工程质量和效率至关重要。桉木芯建筑模板作为市场上的一种选择&#xff0c;与其他材质的模板相比&#xff0c;具有独特的优势。能强优品木业作为专业的桉木芯建筑模板生产厂家&#xff0c;其产品充分展示了这一材质的优越性能。 材质…

计算机组成原理复习5

总线结构与控制 文章目录 总线结构与控制总线结构单总线结构双总线结构三总线结构四总线结构 总线控制判优控制&#xff08;仲裁逻辑&#xff09;链式查询方式计数器定时查询独立请求方式 通信控制同步通信异步通信两种传输率的异步串行传送字符格式 总线结构 单总线结构 单总…

Maven的使用和配置

Maven的使用和配置 起源&#xff1a; Apache 软件基金会(非营业的组织&#xff0c;把一些开源软件维护管理起来) maven 是apache的一个开源项目&#xff0c;是一个优秀的项目构建(管理)工具&#xff0c; maven 管理项目中的jar&#xff0c;以及jar与jar之间的依赖 maven 可…

力扣热题100道-子串篇

字串 560.和为K的子数组 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2示例 2&#xff1a; 输入&a…

前端算法之滑动窗口

滑动窗口 滑动窗口思路&#xff1a;举例: 在一个数组中查找连续子数组的最大值之和。举例: 无重复字符的最长子串 滑动窗口 滑动窗口&#xff08;Sliding Window&#xff09;是一种常用的算法技巧&#xff0c;用于处理数组或字符串相关的问题。 它通过维护一个固定大小的窗口…

亿级流量场景下如何优化混合回收

大白话&#xff1a; 我们完全可以通过最大停顿时间的调整&#xff0c;来控制每次垃圾回收的region数量以及回收垃圾大小。 大白话&#xff1a; 1.是已分配内存占总内存的超过45%阈值&#xff1b; 2.优化G1垃圾回收关键的一点&#xff0c;设置合理的最大停顿时间。 大白话&#…

打破数据孤岛:ChatGPT如何打通金融大数据的任督二脉?

文章目录 一、引言二、ChatGPT与金融大数据分析的融合三、实践应用&#xff1a;ChatGPT在金融大数据分析中的优势与挑战四、案例分析&#xff1a;ChatGPT在金融大数据分析中的应用案例五、前景展望&#xff1a;ChatGPT在金融大数据分析领域的未来发展《AI时代Python金融大数据分…

数据仓库-数仓优化小厂实践

一、背景 由于公司规模较小&#xff0c;大数据相关没有实现平台化&#xff0c;相关的架构都是原生的Apache组件&#xff0c;所以集群的维护和优化都需要人工的参与。根据自己的实践整理一些数仓相关的优化。 二、优化 1、简易架构图 2、ODS层优化 2.1 分段式解析 随着业务增长…

美团面试:索引的设计规范,你知道哪些?

美团面试&#xff1a;索引的设计规范&#xff0c;你知道哪些&#xff1f; 尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;尼恩一直在指导大家改造简历、指导面试。指导很多小伙伴拿到了一线互联网企业网易、美团、字节、如阿里、滴滴、极兔、有赞、希音、百…

智慧园区物联综合管理平台之系统集成功能及接口需求

南向接口 高端制造园区联网平台南向主要面对各园区各项公共物联设施接入。 南向所接入的接口类型、 数据协议等也种类繁多, 平台根据实际调研情况及未来发展趋势, 梳理了十几种设备以及协议接入的情况, 依据此情况, 园区物联管理平台设计了标准的、 统一的南向的标准SDK调…

2023年12月31日_解读openai的新对齐论文

如果人类无法监督超级人工智能 那么人工智能可以么&#xff1f; 过去一年 以预测下一个Token为本质的大模型 已经横扫了人类世界的多项任务 展现了人工智能的巨大潜力 Ilya Sutskever也大胆预言 如果模型能够很好地预测下一个token 那么意味着它能够理解导致这个token产生的深刻…

多边形的填充算法的分析

多边形的填充 分析 思路一 我们之前已经实现了对直线的扫描转换&#xff0c;但是现在我们遇到了新的问题那就是如何对多边形进行填充&#xff0c;如图所示&#xff0c;如何对图示的多边形进行填充呢&#xff1f; 我所想到的第一个办法是这样的&#xff0c;那就是假设上顶点…

webRTC实时通信demo

参考文档&#xff1a; https://www.jianshu.com/p/f439ce5cc0be https://www.w3cschool.cn/socket demo流程示意图&#xff08;用户A向用户B推送视频&#xff09;&#xff1a; #mermaid-svg-0KZaDQ5DBl28zjmZ {font-family:"trebuchet ms",verdana,arial,sans-seri…

总结—elasticsearch启动失败的几种情况及解决

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 摘要 本文主要梳理从ES初学以来所遇到的启动失败的几种情况。 1、使用root用户启动失败 在有一次搭建elasticsearch的时候&am…

除了国家自然科学基金,还有以下科研基金

除了国家自然科学基金&#xff0c;还有以下科研基金&#xff1a; 国家社科基金&#xff1a;这是国家社会科学基金的简称&#xff0c;主要支持国内的社会科学研究项目。国家重点实验室和国家工程&#xff08;技术&#xff09;研究中心开放基金&#xff1a;这些基金主要支持在国…

【Linux】—— 匿名管道

前言&#xff1a; 接下来我将带大家探索 进程间通信 的方式。本期&#xff0c;要讲的就是管道其中之一“匿名管道”&#xff01;&#xff01; 目录 &#xff08;一&#xff09;进程间通信介绍 1、进程间通信目的 2、进程间通信发展 3、进程间通信分类 &#xff08;二&…

(JAVA)-(网络编程)-UDP协议

在上上文章中我们简单了解了UDP通信协议和TCP协议。这篇文章 来讲解java中如何使用UDP协议发送数据。 UDP通信协议发送数据&#xff1a; 而使用UDP通信协议发送数据也可以看成是寄快递的过程。 在寄快递的过程中&#xff0c;我们首先得找快递公司&#xff0c;接着得打包好礼物…

文件分片上传(模拟网盘效果)

文件分片上传&#xff08;模拟网盘效果&#xff09; 文章说明简单模拟拖拽文件夹和选择文件的进度条效果效果展示结合后端实现文件上传效果展示加上分片的效果效果展示加上MD5的校验&#xff0c;实现秒传和分片的效果后续开发说明源码下载 文章说明 文章主要为了学习文件上传&a…

2024年【黑龙江省安全员C证】考试及黑龙江省安全员C证找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年黑龙江省安全员C证考试为正在备考黑龙江省安全员C证操作证的学员准备的理论考试专题&#xff0c;每个月更新的黑龙江省安全员C证找解析祝您顺利通过黑龙江省安全员C证考试。 1、【多选题】下列属于编制安全检查…

浅聊配置化-要不要实现动态表单

1、配置化的原则 配置化是一种抽象&#xff0c;把事物分成2类&#xff1a;不变的&#xff0c;可变的。 如果事物都是可变的&#xff0c;是无法实现配置化的。 配置化的根本在于找到不变的事物&#xff0c;基于不变的事物进行可变事物的配置。 所以&#xff0c;认为一切皆可…