前言
本文展示了欧洲计算机开发商协会正在发展的C++/CLI(一种不同的C++语言它方便开发人员在微软的NET框架下更容易地开发程序)语言在C++语言上的扩展写这篇文章的目的并不是要建议标准C++包括这部分扩展也不是对C++/CLI的认可而只是在探讨C++/CLI语言在这一领域的发展方向
一基础知识
C++/CLI中的属性是类似与各种数据成员(有各种操作限制)的可操作实体但是这种操作往往被转化为调用存取函数(这主要是getter和setter函数)例如
struct Demo {
property int Val { // 一个非常简单的整型分级属性
int get() const {
++Demo::access_count;
return this>value;
}
void set(int v) {
++Demo::access_count;
this>value = v;
}
}
private:
int value;
static unsigned long access_count;
};
int main() {
Demo d;
dVal = ; // 调用set操作函数
return dVal; //调用get函数
}
存取函数的名字必须是get 或者是 set函数两者之中的任何一个都可以被省略但绝不能两者全省略省略一个存取函数导致只存在一个读属性或只存在一个写属性属性的地址是无法获取的然而存取函数作 为成员函数理所当然地可以被用来产生指向成员的指针常量(例如&Demo::Val::set)
属性可以使用关键字virtual进行声明这意味者存取操作函数是虚函数纯虚属性函数也是可能存在的例如
struct VirtualProp {
virtual property int Val = {
int get() const; // 纯虚函数
virtual void set(int v); //纯虚函数这里关键词virtual是多余的
}
//
};
上述例子显示了通常情况下遇到的一些简单的非静态的分层次的属性实例C++/CLI文档包含了大量的概念变化下文将进行解释
二动机
在标准C++的上下文中属性约定成俗地使用get和set函数文法这种文法将暴露的数据和谐地转换为封闭地状态信息在更精细的实时框架上下文中(具体的说是微软的NET框架)属性是可以通过映射实时发觉和修改的元素例如现代的GUI库将它的组件参数声明为属性可视化的界面构筑工具装载这些库使用装载各种组件的属性列表并将结果展现到用户面前当用户修改了一个属性存取操作函数将被调用例如这将触发各种GUI更新事件
三属性变量
除了上述代码中声明的简单的分层属性C++/CLI还引进了其他几种类型属性变量
(一)静态分层属性
静态分层属性使用关键字Static来声明它们的存取操作函数是静态的静态属性的存取操作与静态数据成员的存取操作非常一致(例如使用C::P语法来获取C类的静态属性P)
(二)不明显的分层属性
一个属性的定义(即括号内的存取操作函数声明)可以使用分号来代替在这种情况下get和set存取函数综合成一个简单的可以存取操作的属性值例如C++/CLI定义的一个类如下
struct TrivialProp {
property int Val;
};
上述代码从本质上与下述代码相同
struct TrivialProp {
property int Val {
int get() const { return this>__Val; }
void set(int v) { this>__Val = v; }
}
private:
int __Val;
};
(三)指定索引属性
使用操作数组成员的老语法指定索引可以操作一个数值集合下面的例子显示了一维索引属性的操作
struct Demo {
property int x[std::string] {
int get(std::string s) const { }
void set(int v std::string s) { }
}
//
};
int main() {
Demo d;
std::string s(CLI);
dx[s] = ; // Calls Demo::x::set( s)
return dx[s]; // Calls Demo::x::get(s)
}
注意指定索引的属性不能是静态变量
多维的索引属性也是可以的它引入的操作语法与C/C++中数组元素操作方法不太一样例如
struct Demo {
property double x[std::string int] {
double get(std::string s int n) const { }
void set(double v std::string s int n) { }
}
//
};
int main() {
Demo d;
std::string s(CLI);
dx[s ] = ; // Calls Demo::x::set( s )
return dx[s ] != ; // Calls Demo::x::get(s )
}
后面的这一个例子说明了出现在括号内的操作索引属性的逗号符号是表达式操作符号而不是一个逗号操作符(下面将讨论这种规则带来的后果)
(四)默认的索引属性
除了对象被编入伪域外默认的索引属性与指定的索引属性非常相象对象本身可以索引(仿佛它自身有一个[]操作成员函数一样)以前的代码只要稍微改动一下就可以说明这种变化
struct Demo {
property double default[std::string int] {
double get(std::string s int n) const { }
void set(double v std::string s int n) { }
}
//
};
int main() {
Demo d;
std::string s(CLI);
d[s ] = ; // Calls Demo::default::set( s )
return d[s ] != ; // Calls Demo::default::get(s )
}
请关注关键词default代替属性名的用法
四一些技术性问题
欧洲计算机制造商协会(C++/CLI标准的制订者)已经研究并解决了引入属性所带来的若干问题下面这些内容尤其值得关注
(一)多维索引属性的操作
p>x[ ]表达式拥有不同的意思这要视成员x是否是属性(这种情况下逗号分隔两个索引属性)或其它成员变量(这种情况下逗号是个操作符号表达式的意思等同于p>x[])而定为了在一个属性索引中获取逗号操作符的效果开发人员可以使用圆括号(即p>x[( )])
(注意在依赖模版的表达式中这将产生模糊性并且直到实例化时问题才能得到解决)
(二)属性名与类型名沖突
微软NET框架带有很多包含属性的类(这些类最初并不是使用C++/CLI来开发的)这些包含的属性名与属性类型的名字相同例如
typedef int Color;
struct Conflict {
property Color Color { // Property name hides type name
typename Color get() const;
void set(typename Color);
}
//
};
}
为了帮助在这种上下文中书写代码C++/CLI计划添加语法使用关键词typename来标识不标准的类型(特别是属性)查找标志符的过程中将被忽视上述的代码就以这种新的形式使用typename关键词
(三)重载的索引属性
索引属性可以被重载即几个指定索引属性可以使用同一个名字共存于同一个类中假定它们可以根据属性的类型来区分开来相似地默认的索引属性可以使用其他属性或操作符[]来重载解决两意性与重载行为的规则已经被建立起来来处理上述情况
(四)保留的成员名字
C++/CLI属性通过综合特定的成员来实现这些成员的名义由微软的NET框架来规定并且必须得到保留
如果一个类包含分层的属性或指定索引属性X成员名 get_X 和set_X在类中得到保留(即使属性仅仅包含一个操作函数也是这样)相似地如果一个类包含有一个默认的索引属性类中的成员函数get_Item 和set_Item也将得到保留