在C++中实现属性
原作:Emad Barsoum翻译:虞振祥
下载本文示例源代码本文译自
http://www.codeguru.com/cpp_mfc/Property.html的Implementing a Property in C++
开发测试环境:Visual C++ 7.0, Windows XP sp1, Windows 2000 sp3
摘要本文试着在C++中不使用任何扩展技术模拟C#(或其他语言)中的属性特征。大多数在C++实现属性的库和编译器使用扩展技术,如Managed C++或C++ Builder,或者他们使用如通常函数的set和get方法,但那不是属性。
详述
我们首先看一下什么是属性。一个属性表现为一个字段或者成员变量,但它通过read和write方法或者get和set方法暗中操作变量。
例如,若存在类A和它的属性Count,我可以写如下的代码:
A foo;
Cout << foo.Count;
实际上Count调用它的get函数返回当前的变量值。你可以将属性定为只读(你可以读取它但不能修改它)、只写或者可读写,这就是使用属性而不直接使用变量的的一个最大好处了。好了,让我们开始来实现它:
我们需要能做如下的事:
int i = foo.Count; //--调用get函数得到值
foo.Count = i; //-- 调用set函数设定值
因此,很明显的我们需要重载''=''操作符使其能设定变量的值,同时也要重载该属性的返回值(在下面我们将会看到的)。
我们将实现一个称为property的类,它做的就像一个属性,声明如下:
template <typename Container, typename ValueType, int nPropType>
class property {}
这个模板类表示的是我们的属性。Container是我们要在其中包含属性的类变量,set和get方法以及属性的类的类型。ValueType是内部变量即要定义的属性的类型,nPropType定义属性的读写标志:只读、只写或可读写。 现在我们需要一个指向从包含属性的类Container到属性类property的set和get方法的指针,同时重载''=''操作符以使得属性能象变量起那样作用。现在我们来看property类的全部定义
#define READ_ONLY 1
#define WRITE_ONLY 2
#define READ_WRITE 3
template <typename Container, typename ValueType, int nPropType>
class property
{
public:
property()
{
m_cObject = NULL;
Set = NULL;
Get = NULL;
}
//-- 将m_cObject指向包含属性的container类 --
void setContainer(Container* cObject)
{
m_cObject = cObject;
}
//-- 设定可改变属性值的set成员函数 --
void setter(void (Container::*pSet)(ValueType value))
{
if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
Set = pSet;
else
Set = NULL;
}
//-- 设定可检索属性值的get成员函数 --
void getter(ValueType (Container::*pGet)())
{
if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
Get = pGet;
else
Get = NULL;
}
//-- 重载''=''号操作符使其能用set成员设定属性值--
ValueType operator =(const ValueType& value)
{
assert(m_cObject != NULL);
assert(Set != NULL);
(m_cObject->*Set)(value);
return value;
}
//-- 使属性类能转换为内部类型成为可能--
operator ValueType()
{
assert(m_cObject != NULL);
assert(Get != NULL);
return (m_cObject->*Get)();
}
private:
Container* m_cObject; //-- 指向包含属性的类模块 --
void (Container::*Set)(ValueType value);
//-- 指向set成员函数的函数指针 --
ValueType (Container::*Get)();
//-- 指向get成员函数的函数指针 --
};
现在让我们来一段一段地看这些代码:
在下面的代码中,仅仅将Container指针指向一个有效的包含属性的实例。
void setContainer(Container* cObject)
{
m_cObject = cObject;
}
下面的代码,设定指针指向包含属性的类中的set和get成员函数,其set和get成员函数度有,唯一的限制即set成员函数必须有一个ValueType型的参数并无返回值,get成员函数没有参数,但要返回ValueType型值。
//-- 设定可改变属性值的set成员函数 --
void setter(void (Container::*pSet)(ValueType value))
{
if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE))
Set = pSet;
else
Set = NULL;
}
//-- 设定可检索属性值的get成员函数 --
void getter(ValueType (Container::*pGet)())
{
if((nPropType == READ_ONLY) || (nPropType == READ_WRITE))
Get = pGet;
else
Get = NULL;
}
在如下的代码中,第一部分是''=''操作符的重载,它调用包含属性的类中的set函数设定其属性的值。第二部分则为了使整个属性类象ValueType类型一样起作用,所以它返回包含属性的类中get函数的返回值。
//-- 重载''=''号操作符使其能用set成员设定属性值--
ValueType operator =(const ValueType& value)
{
assert(m_cObject != NULL);
assert(Set != NULL);
(m_cObject->*Set)(value);
return value;
}
//-- 使属性类能转换为内部类型成为可能--
operator ValueType()
{
assert(m_cObject != NULL);
assert(Get != NULL);
return (m_cObject->*Get)();
}
现在我们来看看怎样使用它:
如下所示,在PropTest类中定义了一个叫做Count的简单属性。Count的实际值将保存到或检索之在PropTest的私有成员变量"m_nCount"中,通过PropTest的get和set方法。get和set方法可以使用任何的变量名字,只需他们的地址能被传递到property类中,如下面的PropTest构造函数里面的代码般,代码行" property<PropTest, int, READ_WRITE> Count; "让我们在PropTest中得到可读写的int型的Count属性。现在你可以使用如一般的成员变量般使用使用Count属性了,但实际上你是间接地调用它set和get方法。
要使Count属性能成功工作,必须先在PropTest的构造函数里面对其进行初始化。
class PropTest
{
public:
PropTest()
{
Count.setContainer(this);
Count.setter(&PropTest::setCount);
Count.getter(&PropTest::getCount);
}
int getCount()
{
return m_nCount;
}
void setCount(int nCount)
{
m_nCount = nCount;
}
property<PropTest,int,READ_WRITE> Count;
private:
int m_nCount;
};
如下所示,你可以象使用普通变量一样使用Count属性。
int i = 5,j;
PropTest test;
test.Count = i; //-- 调用set函数 --
j= test.Count; //-- 调用get函数 --
要使用只读的属性,你可以创建如下的property实例:
property<PropTest,int,READ_ONLY > Count;
要使用只写的属性,你可以创建如下的property实例:
property<PropTest,int,WRITE_ONLY > Count;
注意:如果你将某一属性设为只读,当你对其赋值时,将引发assertion诊断。同理,当读取只写的属性时
也同样会引发assertion诊断。
小结本文展示了在C++只用标准的C++特性而不使用其他任何的扩展技术来实现属性。当然,直接使用set和get函数效率
要更高些,因为本文中的方法需要为每一个属性定义一个property类实例。
分享到:
相关推荐
本项目“C++实现属性表单和向导生成”聚焦于如何利用C++来构建用户界面(UI),特别是属性表单和向导,这对于创建用户友好的应用程序至关重要。 属性表单通常用于设置或修改对象的属性。它们展示一系列的控件,如...
通过上述方法,我们可以在C++中实现对文件属性和时间戳的修改。这对于文件管理系统、日志记录或者任何需要控制文件状态的应用场景都十分有用。在实际开发中,应确保遵循安全编程原则,避免因误操作导致的数据丢失或...
在本文中,我们将对 C++Builder 组件的常见属性进行详细的解释。 一、 Caption 属性 Caption 属性是窗体和可视化控件的共有属性,用来指定窗体标题栏中的说明文字。它的默认值与控件名相同,但程序员可以在对象...
在C++实现中,通常会包含以下四个类: 1. **DataSet类**:这是整个系统的基础,用于存储原始数据。它通常包含数据实例的集合以及每个实例的属性值。这个类需要提供添加、删除和访问数据实例的方法,以及获取属性...
在C++中实现Canny算子通常涉及到以下几个关键步骤: 1. **噪声过滤**:首先,使用高斯滤波器来平滑图像,以减少噪声对边缘检测的影响。高斯滤波器的核大小和标准差可以根据实际图像的噪声水平来选择。 2. **计算...
在C++中实现蓝牙通讯,我们需要理解这些层的工作原理,并能够用C++语言来模拟它们的功能。 1. **物理层**:这是蓝牙通信的底层,定义了无线射频(RF)规范,如频率分段、调制方式和功率控制。在C++中,我们可能需要...
在C++编程语言中,高级属性是提升代码效率和可维护性的关键要素。这个资源集合涵盖了C++的一些核心概念,包括模板、继承、多态、动态内存管理以及类的高级特性。下面将对这些主题进行深入讲解。 1. **C++模板**:...
在Qt框架中,QML(Qt Meta Language)和C++是两种主要的编程语言,它们在构建用户界面和实现业务逻辑方面各自发挥着重要作用。QML以其声明式语法和直观的UI设计,使得构建现代、动态的应用变得简单,而C++则提供了...
在C++编程中,属性表(Property Sheet)通常用于创建多页对话框,它允许用户在不同的页面上配置不同类型的设置。属性表是Windows GUI应用程序中常见的一种用户界面元素,可以提供良好的组织和导航功能。VC++是...
在C++实现中,我们可以使用如 `std::vector` 和 `std::unordered_set` 这样的容器来存储和操作数据点。`std::vector` 可用于存储数据点集合,`std::unordered_set` 可用于快速查找邻域点。计算邻域通常可以通过空间...
C++实现数据库DBMS建表插入删除属性功能
下面我们将详细探讨这个C++实现的五子棋游戏涉及的知识点。 1. **C++基础语法**:项目的实现基于C++语言,因此首先需要了解C++的基本语法,包括变量声明、数据类型、流程控制语句(如if、for、while)、函数定义与...
在C++编程中,属性对话框模板是一种常用的技术,它允许开发者创建用户界面,这些界面可以方便地编辑和查看对象的属性。属性对话框通常由一系列控件组成,如文本框、复选框和组合框,用于显示和修改对象的各种特性。...
在C++实现过程中,我们需要注意以下几点: - **面向对象编程**:C++是一种面向对象的语言,我们可以创建电梯和乘客类,封装它们的行为和属性,通过成员函数来处理各种操作。 - **异常处理**:考虑到可能出现的错误...
在这个项目中,C++被用来定义和操作数据结构,如学生对象,以及实现各种功能,如成绩计算和存储。 2. **面向对象编程(OOP)**:在学生成绩管理系统中,面向对象编程思想是核心。可以创建一个`Student`类来表示学生...
在本项目中,“驾驶证管理C++实现”是一个利用C++编程语言设计的系统,用于管理和操作驾驶员的相关信息。这个系统可能包含了对驾驶证的增删改查等基本功能,旨在提高驾驶证信息管理的效率和准确性。下面我们将深入...
在C++实现过程中,需要设计类来表示数据样本、属性、决策树节点等,并实现相应的函数来执行上述步骤。例如,可以定义`DataSample`类存储样本的特征和类别,`Attribute`类表示属性,`TreeNode`类表示决策树节点,其中...
本项目"用户权限管理模块(C++实现)"是一个用C++编程语言实现的权限管理解决方案,它包含了几个核心功能,如单例模式、用户区分、用户管理以及密码安全存储。 首先,我们来深入理解C++实现的用户权限管理模块。C++...
在本项目中,"CAN_BUS c++实现 源代码"提供了用C++编程语言实现CAN总线功能的示例代码,这对于理解和学习CAN总线通信机制以及C++编程技巧非常有帮助。 在C++中实现CAN总线通信,通常需要使用特定的硬件接口,如CAN...
在本文中,我们将深入探讨如何使用C++编程语言和MFC(Microsoft Foundation Classes)库来实现一个五子棋游戏。MFC是微软提供的一套面向对象的类库,用于简化Windows应用程序开发,它封装了许多Win32 API,使得...