C++/CLI学习入门(九):类的属性(转…

属性是C++/CLI的类 成员,它类似于成员变量,但实际上不是。其主要区别在于,字段名引用了某个存储单元,而属性名则是调用某个函数。属性拥有访问属性的set()和 get()函数。当我们使用属性名时,实际上在调用该函数的get()或set()函数。如果一个属性仅提供了get()函数,则它是只读属性;如果一个 属性仅提供set()函数,则它是只写属性。

类可以有2种不同的属性:标量属性 和索引属性。标量属性是指通过属性名来访问的单值;索引属性是利用属性名加方框号来访问的一组值。如 String类,其Length属性为标量属性,用object->Length来访问其长度,且Length是个只读属性。String还包含了 索引属性,可以用object[idx]来访问字符串中第idx+1个字符。

属性可以与类的实例(类对象)相关,此时属性被称为实例属性,如String类的Length属性;如果用static修饰符指定属性,则属性为类属性,所有该类得实例都具有相同的属性值。

一、标量属性

标量属性是单值,用property关键字可定义标量属性,还需要定义其get()和set()函数,如下例所示

value class Height
{
private:
// Records the height in feet and inches
int feet;
int inches;
literal int inchesPerFoot = 12;
literal double inchesToMeter = 2.54/100;

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

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

// The height in meters as a property
property double meters
{
double get()
{
return inchesToMeters * (feet*inchesPerFoot + inches);
}
}

// Create a string representation of the object
virtual String^ ToString() overrides
{
return feet + L" feet " + inches + L" inches";
}
};

上面的例子定义了一个merters的属性,下面是属性的用法

Height ht = Height(6, 8);
Console::WriteLine(L"The height is {0} meters", ht->meters);

属性不一定要定义成内联函数,也可以在.cpp中外部定义它,如在上例的定义中仅保留get()函数声明

property double meters
{
double get();
}

函数定义在.cpp中时,需要加类名和函数名的限定(但不需要返回值?),方法如下:

Height::meters::get()
{
return inchesToMeters*(feet*inchesPerFoot+inches);
}

如果定义一个属性时,不提供get()和set()函数定义,这种属性被称为平凡标量属性。对于此类属性,编译器将提供一个默认的get()和set()实现,如下例所示:

value class Point
{
public:
property int x;
proterty int y;

virtual String^ ToString() overrides
{
return L"("+x+L","+y+")"; // Result is (x,y)
}
};

下面是一个完整的例子,说明了标量属性的声明及其使用方法

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

#include "stdafx.h"

using namespace System;

// Class defining a person's height
value class Height
{
private:
// Record the height in feet and inches
int feet;
int inches;

literal int inchesPerFoot = 12;
literal double inchesToMeters = 2.54/100;

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

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

// The height in meters
property double meters
{
double get()
{
return inchesToMeters*(feet*inchesPerFoot+inches);
}
}

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

// Class defining a person's weight
value class Weight
{
private:
int lbs;
int oz;

literal int ouncesPerPound = 16;
literal double lbsToKg = 1.0/2.2;

public:
Weight(int pounds, int ounces)
{
lbs = pounds;
oz = ounces;
}

property int pounds
{
int get() { return lbs; }
void set(int value) { lbs = value; }
}

property int ounces
{
int get() { return oz; }
void set(int value) { oz = value; }
}

property double kilograms
{
double get() { return lbsToKg*(lbs+oz/ouncesPerPound); }
}

virtual String^ ToString() override
{
return lbs + L" pounds " + oz + L" ounces";
}
};

ref class Person
{
private:
Height ht;
Weight wt;

public:
property String^ Name;

Person(String^ name, Height h, Weight w) : ht(h), wt(w)
{
Name = name;
}

Height getHeight() { return ht; }
Weight getWeight() { return wt; }
};

int main(array<System::String ^> ^args)
{
Weight hisWeight = Weight(185, 7);
Height hisHeight = Height(6, 3);
Person^ him = gcnew Person(L"Fred", hisHeight, hisWeight);

Weight herWeight = Weight(105, 3);
Height herHeight = Height(5, 2);
Person^ her = gcnew Person(L"Freda", herHeight, herWeight);

Console::WriteLine(L"She is {0}", her->Name);
Console::WriteLine(L"Her weight is {0:F2} kilograms.", her->getWeight().kilograms);
Console::WriteLine(L"Her height is {0} which is {1:F2} meters.", her->getHeight(),
her->getHeight().meters);

Console::WriteLine(L"He is {0}", him->Name);
Console::WriteLine(L"His weight is {0}", him->getWeight());
Console::WriteLine(L"His height is {0} which is {1:F2} meters.", him->getHeight(),
him->getHeight().meters);

return 0;
}- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex7_16.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -输出为She is Freda
Her weight is 47.73 kilograms.
Her height is 5 feet 2 inches which is 1.57 meters.
He is Fred
His weight is 185 pounds 7 ounces
His height is 6 feet 3 inches which is 1.91 meters.二、索引属性

索引属性是类的一组属性值,其访问方法同数组元素那样,在方括号内加索引值来访问。如果在方括号前面的是类对象的名称,则该索引属性被称为默认索引属性 (如String^ obj可以用obj[idx]来访问字符串中第idx+1个字符),如果用属性名[idx]来访问索引属性值,则称为有名索引属性。下面的代码在类 Name中定义了一个默认索引属性,

ref class Name
{
private:
array<String^>^ Names;

public:
Name(...array<String^>^ names) : Names(names) {}

// Indexed property to return any name
property String^ default[int]
{
// Retrieve indexed property value
String^ get(int index)
{
if(index >= Names->Length)
throw gcnew Exception(L"Index out of range");
return Names[index];
}
}
};

在上面的例子中,如果将default换成别的名字,则该属性就成为一个有名索引属性。在定义索引属性时,方括号内用int指定了索引的数据类型为int 型,它也可以是别的数据类型。访问索引属性的get()函数的形参其数据类型必须与属性名后方括号内类型相同;set()函数必须有2个形参,第一个指定 索引,第二个指定属性元素的值。

下面是一个完整的例子,说明了索引属性的定义与使用方法。

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

#include "stdafx.h"

using namespace System;

ref class Name
{
private:
array<String ^>^ Names;

public:
Name(...array<String ^>^ names) : Names(names) {}

// Scalar property specifying number of names
property int NameCount
{
int get() { return Names->Length; }
}

// Indexed property to return names
property String^ default[int]
{
String ^ get(int index)
{
if(index >= Names->Length)
throw gcnew Exception(L"Index out of range");
return Names[index];
}
}
};

int main(array<System::String ^> ^args)
{
Name^ myName = gcnew Name(L"Ebenezer", L"Isaiah", L"Ezra", L"Inigo", L"Whelkwhistle");

// List the names
for(int i=0; i<myName->NameCount; i++)
Console::WriteLine(L"Name {0} is {1}", i+1, myName[i]);
return 0;
}- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex7_17.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -输出为Name 1 is Ebenezer
Name 2 is Isaiah
Name 3 is Ezra
Name 4 is Inigo
Name 5 is Whelkwhistle

索引属性的索引也可以不是整型,甚至可以不是数字,下面的例子定义了一个商店类,其属性Opening指定了商店的开门时间,访问该属性的索引有2个参数,如下面的例子所示

enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };

// class defining a shop
ref class Shop
{
public:
property String^ Opening[Day, String^] // Opening times
{
String ^ get(Day day, String^ AmOrPm)
{
switch(day)
{
case Day::Saturday:
if(AmOrPm == L"am")
return L"9:00";
else
return L"14:30";
break;

case Day::Sunday:
return L"closed";
break;

default:
if(AmOrPm == L"am")
return L"9:30";
else
return L"14:00";
break;
}
}
}
};使用该类的方法如下Shop^ shop = gcnew Shop;
Console::WriteLine(shop->Opening(Day::Saturday, L"pm");三、静态属性

静态属性为类的所有实例共有,类似于类的静态成员变量。通过在属性定义前添加修饰符static来定义,如下面的例子所示

value class Length
{
public:
static property String ^ Units
{
String ^ get() { return L"feet and inches"; }
}
};

无论是否创建类实例,静态属性都存在。如果已经定义了类实例,则可以用实例名.属性名来访问静态属性。对于上面的例子如果已经定义了一个类对象len,则可以如此访问其静态属性:

Console::WriteLine(L"Class units are {0}.", len.Units);

?注意:在定义了属性之后,对应的get_属性名和set_属性名自动成为系统保留名称,不能为其它目的而使用他们。如果定义了默认索引属性,则set_Item和get_Item也成为系统保留名称,不可被使用。

<script type="text/javascript" id="wumiiRelatedItems"> </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值