C++/CLI学习入门(八):类的分类与定…

本文详细介绍了C++/CLI中的数值类和引用类,包括它们的特点、定义方法及使用示例。数值类用于表示具有有限数据成员的简单对象,而引用类更接近本地C++类,具有更多灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++/CLI中可以定义 两种类型的struct和class类型,一种为数值类(或数值结构):value class(value struct);一种是引用类(或引用结构):ref class(ref value)。与本地C++一样,class与struct的区别在于前者的成员默认为私有,后者默认为公有。下面仅以类来介绍,内容同样适用于结构。

value class与ref class组成的是双关键字,也就是说,单独的value、ref并不是关键字。数值类与引用类的区别,以及它们与本地C++类的区别主要包括以下几个方面:

  • 数值类的对象包含自己的数据,引用类的对象只能用句柄来访问
  • 在C++/CLI中,函数成员不能声明为const类型,取而代之的是字面值类型,修饰词关键字为 literal。
  • 在非静态函数成员中,this指针类型与本地C++不同:数值类的this指针为内部指针类型(interior_ptr<T>),而引用类的this指针为句柄类型(T^)。
  • C++/CLI类的数据成员不能包含本地C++数组或本地C++类对象。
  • C++/CLI类无友元函数。
  • C++/CLI类的数据成员不能包含位类型的数据成员。(什么是位数据类型)
  • C++/CLI类的函数成员不能有默认的形参。

此外,在C++/CLI中,不推荐类命名时使用前缀‘C’,其成员变量命名也不用前缀’m_’。

一、定义数值类

数值类主要用于表示具有有限个数据成员的简单对象,其定义方法与本地C++类基本相同。首先看一个定义数值类,以及使用类的完整例子。

- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::开始==>> [Ex7_14.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Ex7_14.cpp : main project file.

#include "stdafx.h"

using namespace System;

// class representing a height
value class Height
{
private:
// Records the height in feet and inches
int feet;
int inches;

public:
// Create a height from inches value
Height(int ins)
{
feet = ins/12;
inches = ins%12;
}

// Create a height fromm feet and inches
Height(int ft, int ins) : feet(ft), inches(ins) {}
};

int main(array<System::String ^> ^args)
{
Height myHeight = Height(6, 3);
Height^ yourHeight = Height(70);
Height hisHeight = *yourHeight;

Console::WriteLine(L"My height is {0}", myHeight);
Console::WriteLine(L"Your height is {0}", yourHeight);
Console::WriteLine(L"His height is {0}", hisHeight);
return 0;
}- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex7_14.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

输出为

My height is Height
Your height is Height
His height is Height

在上面的例子中,myHeight和hisHeight被分配在堆栈 上,yourHeight被分配到了CLR堆上。其中hisHeight是yourHeight的一个副本,当向 hisHeight赋值时,需要用*操作符对句柄yourHeight进行解除引用计算。这是因为数值类对象总是包含自己的数据,因此它们不能引用同一个对象,在赋值时总是采用复制的方式进行。注意:在C++/CLI中,不能重写默认构造函数。默认构造函数将所有的值类型数据成员初始化为0,将引用类型(句柄)初始化为nullptr。同样,也不能重载复制构造函数和赋值操作符。默认的复制操作是将每一个数据成员进行复制,对象间的赋值也是如此

C++/CLI中的类都有一个成员函数ToString(),它返回一个表示对象的字符串 句柄。默认情况下,该字符串为类名。这从上面的输出可以看出:传递给WriteLine()函数的是Height对象,结果输出的并非对象所包含的高度 值,而是类名Height,这是因为编译器认为此处需要调用该对象的字符串表示法,因此安排的ToString()函数调用,这个过程可以显示的表达为

double pi = 3.142;
Console::WriteLine(pi.ToString());

double类型被映射到System命名空间中的System::Double类,该类 实现了ToString方法,因此可以正确的输出变量pi的数值3.142而非类名Double。在上面的例子中,为了正确地输出高度,可给Height 定义中增加ToString()的重载函数。

//Create a string repesentation og the object
virtual String^ ToString() override
{
return feet + L" feet " + inches + L" inches";
}

现在可以正确的输出为

My height is 6 feet 3 inches
Your height is 5 feet 10 inches
His height is 5 feet 10 inches

在定义数值类时,如果数据成员为常量,C++/CLI中将其定义为”字面值” (literial)。在上面的例子中,将12定义为字面值,可以使得代码的可读性更高,避免“幻数”的出现(意指程序代码中难以理解其来源或意义的常 数,如上面例子中的12)。定义字面值的方法如下

value class Height
{
int feet;
int inches;
literial int inchesPerFoot = 12;

// Other code...
};

这样就可以在其后直接使用该字面值,而非难以理解的12了

Height(int ins)
{
feet = ins/ inchesPerFoot;
inches = ins% inchesPerFoot;
}

利用”字面值”leterial来定义常量的一个缺点是:必须在定义常量的同时指定它的值。另外一种定义常量的方法是使用initonly修饰符,使用该修饰符的常量变量只能在构造函数的初始化表,或者构造函数体内进行一次初始化, 之后再也不能被修改。注意:不能在声明非静态initonly常量时指定初值,而必须是在构造函数的初始化表或构造函数体内。下面的例子描述了onlyinit的用法

value class Length
{
private:
int feet;
int inches;

public:
initonly int inchesPerFoot;

// Constructor
Length(int ft, int ins) :
feet(ft), inches(ins),
inchesPerFoot(12);
}上面的构造函数也可以写成Lenght(int ft, int ins) :
feet(ft), inches(ins)
{
inchesPerFoot = 12;
}

如果是静态地initonly变量,则只能在定义时指定初值。因为如果自构造函数中定义,则每次创建类实例都将对静态变量赋值,这显然与静态、常量这样的概念冲突。解决的办法是,如果一定要在构造函数中初始化initonly类型的静态常量,则可定义一个静态构造函数。

value class Length
{
private:
int feet;
int inches;

static Length() { inchesPerFoot = 12; }

public:
initonly static int inchesPerFoot;

Length(int ft, int ins) :
feet(ft), inches(ins)
{ }
};

静态构造函数函数没有形参,且没有初始化表,总是被声明为private。它不能被直接调用,而是由普通构造函数在调用之前自动调用。这种方法与在定义静态initonly变量时指定初始值的唯一区别是,初始化值可以是在运行时确定的。

二、定义引用类

引用类更加类似于本地C++类,它没有数值类那么多的限制。但引用类没有默认的复制构造函数和赋值运算符,如果定义的类需要进行复制或赋值,必须显式地添加相应的函数成员。下面的例子定义了一个引用类及其使用方法。

- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::开始==>> [Ex7_15.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Ex7_15.cpp : main project file.

#include "stdafx.h"

using namespace System;

ref class Box
{
public:
// No-arg constructor supplying default field values
Box():Length(1.0), Width(1.0), Height(1.0)
{
Console::WriteLine(L"No-arg constructot called.");
}
// Constructor definition using an initialisation list
Box(double lv, double bv, double hv):Length(lv), Width(bv), Height(hv)
{
Console::WriteLine(L"Constructor called.");
}

// Function to calculate the volume of a box
double Volume()
{
return Length*Width*Height;
}

private:
double Length;
double Width;
double Height;
};

int main(array<System::String ^> ^args)
{
Box^ aBox;
Box^ newBox = gcnew Box(10, 15, 20);
aBox = gcnew Box;

Console::WriteLine(L"Default box volume is {0}", aBox->Volume());
Console::WriteLine(L"New box volume is {0}", newBox->Volume());
return 0;
}- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex7_15.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

输出为

Constructor called.
No-arg constructot called.
Default box volume is 1
New box volume is 3000

在上面的例子中,main()函数的第一句没有创建任何对象,仅仅声明了一个句柄,并被默认的赋值成nullptr。此外,引用对象总是在堆上创建,因此总是用gcnew来调用其构造函数,并用句柄来跟踪引用对象。

<script type="text/javascript" id="wumiiRelatedItems"> </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值