首页 > 编程语言 >深入探究C++ 类成员(Class Members)

深入探究C++ 类成员(Class Members)

时间:2024-05-06 21:44:35浏览次数:14  
标签:Members object C++ class member static members data Class

一、定义

  在class的声明里头,真正有用的两样东西是data members 和 member functions:

  Data members:表示根据这个class所产生的object里头会有些什么东西,它事实上也是占据object内存的唯一东西(除非引入虚拟机制)。通常为数据的封装性,我们把data members声明为private或protected。

  Member functions:是用来处理data members的函数。通常为了界面的开放性,我们把member functions设计为pubilc。

二、Data Members(数据成员)

  Data members的声明和一般non-class的变量声明形式一样,以CPoint为例:

 1 class CPoint
 2 {
 3 public:
 4     CPoint(float x=0.0):_x(x){}
 5     
 6     float x(){return _x;}
 7     void x(float xval){_x=xval;}
 8     
 9 protected:
10     float _x;     //data member
11     
12 };

  第10行的_x就是一个data members。

  如果data member本身不是一个变量,而是一个class object,这种情况比较特殊,称为composition(组合),而这种object被称为embedded objects或object member。Composition被用来描述has a的关系(例如汽车有一个引擎),inheritance(继承)用来描述is a kind of的关系(例如汽车是一种交通工具)。稍后我们会分别描述composition和inheritance两种情况。

三、Member Functions(成员函数)

  Member functions用来处理class data members。在数据封装的情况下,member functions可以说就是一个class(或object)的对外界面。这些functions并不占用object的内存空间,它被编译器处理过后,以完全等同一般的global functions的身份出现,独立于任何object之外。同一份函数代码如何能够处理依据同一个class产生出来的不同object的data members呢?关键在于有一个this指针隐藏在member functions的参数之中,也隐藏在member functions函数代码对于data members的处理动作上,是编译器自动为我们加上去的。

 四、如何取用 Class Members

  要取得class members,如果是在class scope之内,直接使用其名称即可。但如果是在class scope之外,你有三种方式可以办到:

  1.透过dot operator(.),例如:

1 CPoint aPoint; // 产生一個 CPoint object,名为 aPoint
2 aPoint.x(); // 唤 起 CPoint::x()
3 aPoint._x = 7.0; // 使 用 CPoint 的 data member

  2.透过arrow operator(->),例如:

1 CPoint* pPoint = new CPoint; // 产生一个 CPoint object,由 pPoint 指向它
2 pPoint->x(); // 唤 起 CPoint::x()
3 pPoint->_x = 7.0; // 使用 CPoint 的 data member

  3.透过scope resolution operator(::),例如:

1 CPoint::foo(); // foo() must be a static member function
2 CPoint::ratio = 0.2; // ratio must be a static data member

  第三种方法未通过任何对象访问成员,表明这些成员应属于类层次,即静态成员(Static Members)。若错误地通过对象访问非静态成员,如CPoint::foo(),将引发错误。静态成员应通过类名访问,非静态成员应通过对象实例访问。

五、特殊的 Static Members(静态成员)

  Class中可以使用static关键,使members成为静态。

  静态的意思是这份members属于class而非object所有。听起来有点奇怪,因为class只是属性,而object才是实体。很难一言以敝之,让我们将members细分为data 和function来讨论。

  当你看完以下的剖析,你会认为static data members和global variables很像,static member functions和global functions 很像。一点没错,从功能而言,global 可以取代static。不过,以面对对象的观点来看,这些members如查在性质上与class息息相关,不是设计成class的static members为佳,面对对象的程序设计中,我们希望global的东西越少越好。

1. Static Data Members(静态数据成员)

  虽说data属于object实体所有,但程序设计中有些数据也希望由各object共享,所以提升到class层级比较理想。例如我们设计一个银行储户的class:

1 class SavingAccount
2 {
3 private:
4     char   m_name[40];//储户姓名
5     char   m_addr[60];//储户地址
6     double m_total;//账户金额
7     double m_rate;//利率
8 };

  这家银行采用浮动利率,每个账户的利息是根据当天的利率来计算的,这时m_rate就不适合成为每个object的数据了,否则每天一开市,光把所有的账户内容调出来,修改m_rate的值,就花了不少时间。m_rate应该独立于各object之外,成为class的独一无二的数据;具体怎么做?在m_rate前面加上Static关键字就行了:

1 class SavingAccount
2 {
3 private:
4     char   m_name[40];//储户姓名
5     char   m_addr[60];//储户地址
6     double m_total;//账户金额
7     Static double m_rate;//利率(注意,这里只是声明,不是定义,未分配内存)
8 };

  Static data members不属于object的一部份,而是class的一部份,所以程序可以在还没有生成任何object的时候就处理Static data members。但首先,你必须定义它(为它分配内存)。

你应该在.CPP文件中且在class以外的任何区域设定其初始值,例如在main()之中,或在global function中,或任何函数之外:

1 double SavingAccount::m_rate = 0.0075; // 定义并设定初值
2 void main() // 不指定初值也可以
3 {
4 ...
5 }

  请注意用词上的严谨:上述“定义”动作常被误以为是“初始化”动作。如果只是单纯的初值设定动作,应该可以安排在class constructor中,但上一行绝对无法如此。此外,你也不应该把上述的定义安排于.H文件中,因为这么一来它可能会被含入许多.CPP文件中,于是就重复定义了(会发生连接错误)。

  上述动作有没有考虑m_rate是private数据呢?没有关系,定义static data members,不受任何封装层级的约束。请注意上述动作也指出static data members的类型,因为这是一个定义动作,不是一个数值指定动作,如果没有做这个动作,m_rate就没有获得内存,会发生异常错误:

error LNK2001: unresolved external symbol "private: static double
SavingAccount::m_rate"(?m_rate@SavingAccount@@2HA)

  下面是存取static data members的一种方式,注意:此时还没有任何object存在:

1 void main()
2 {
3 SavingAccount::m_rate = 0.0072; // 此行要成立,m_rate 需改为public
4 }

   第二种方式是产生一个object后,透过object来处理static data members:

1 void main()
2 {
3 SavingAccount myAccount;
4 myAccount.m_rate = 0.0072; // 此行要成立,m_rate 需改为 public
5 }

  请注意,static data members并不是因为myAccount object的实现而才得以实现,它本来就存在。因此第一种的处理方式不容易给人错误印象;

2. Static Menber Functions(静态成员函数)

  既然static data members 是超越于object之外存在的,一般的member functions能否处理它呢?要知道,,member function得经由某个object才能调用,该object地址则成为所谓的this指针,但由于static data members并不需要靠this指针的指引来决定其地址,所以其实任何函数(包含global函数),只要access level允许,都可以处理static data members。

  但member function得经由某个object才能调用,如果你在尚未产生任何object之前,就希望透过某个member function来更我以为前例的static m_rate,而它又是private(以至于不能被global函数处理)那么一定要写个static member function了:

 1 class SavingAccount
 2 {
 3 private:
 4     char   m_name[40];//储户姓名
 5     char   m_addr[60];//储户地址
 6     double m_total;//账户金额
 7     Static double m_rate;//利率(注意,这里只是声明,未分配内存)
 8 public:
 9     static void setRate(double newRate){m_rate=newRate;}
10 };
11 
12 double SavingAccount::m_rate=0.0072; //设初值
13 
14 int main()
15 {
16     SavingAccount myAccount;
17     //可以透过object调用static member functions
18     myAccount.setRate(0.0072);
19     //也可以直接调用static memberfunction
20     SavingAccount::setRate(0.0072);
21     
22     return 0;
23 }

  由于static member functions 不需要任何object的存就可以被调用,所以编译器不会为它暗加一个this指针。也因为如此,static member function无法处理任何non-static data members,因为member function之所以能够以单一一份函数代码处理各个object的数据而不乱,完全靠的是this指针。

3. Static Object Members(静态对象成员)

  Object members应该被视为和一般的data members 样地位,只不过它是class类型,而一般data members是内建类型。当它冠上static,表示它所寄生的class一旦声明完成(未产生object),并且对static members做了初始化动作,这个static object member就已经在内存中有了实体。例如:

  

 1 class Point3d
 2 { public:
 3 // ...
 4 public: // (protected: is better)
 5 static Point3d origin;
 6 float x, y, z;
 7 };
 8 Point3d Point3d::origin; // 別忘了这个动作(其实就是实体配置)
 9 
10 int main()
11 {
12     printf("&Point3d::x = %p\n", &Point3d::x); // 00000004
13     printf("&Point3d::y = %p\n", &Point3d::y); // 00000008
14     printf("&Point3d::z = %p\n", &Point3d::z); // 0000000C
15     printf("&Point3d::origin = %p\n", &Point3d::origin); // 0040A450
16     printf("&Point3d::origin.x = %p\n", &Point3d::origin.x); // 0040A454
17     printf("&Point3d::origin.y = %p\n", &Point3d::origin.y); // 0040A458
18     printf("&Point3d::origin.z = %p\n", &Point3d::origin.z); // 0040A45C
19     Point3d pt3d;
20     cout << "&pt3d.x = " << &pt3d.x << endl;
21     cout << "&pt3d.y = " << &pt3d.y << endl;
22     // 0x0063FDEC
23     // 0x0063FDF0
24     cout << "&pt3d.z = " << &pt3d.z << endl; // 0x0063FDF4
25     
26 }

4. Static Objects(静态对象)

  static 关键字也可以应用在object身上,像这样:

static CPoint aPoint(3.6);

  aPoint于是就成为一个static object,这个object的生命将持续到整个程序结束为止。

 

标签:Members,object,C++,class,member,static,members,data,Class
From: https://www.cnblogs.com/ruanchunyi/p/18176001

相关文章

  • C++面试笔记 - (二)
    1、C++从代码到可执行二进制文件的过程C++和C语言类似,一个C++程序从源码到执行文件,有四个过程,预编译、编译、汇编、链接。预编译:这个过程主要的处理操作如下:将所有的#define删除,并且展开所有的宏定义处理所有的条件预编译指令,如#if、#ifdef处理#include预编译指令,将被包含的......
  • C++面试总结(一)--c与c++不同
    C++面试总结(一)--C与C++不同c++特点C++在C语言基础上引入了面对对象的机制,同时也兼容C语言。C++有三大特性(1)封装。(2)继承。(3)多态;C++语言编写出的程序结构清晰、易于扩充,程序可读性好。C++生成的代码质量高,效率高,C++更加安全,增加了const常量、引用、四类cast转换(stat......
  • c++中文编码问题
    std::string或者constchar*,本质上都是二进制,不包含编码属性,其编码信息来源于赋值语句,QString以utf16编码,默认构造或赋值的字面量假定为utf8,若是其它编码比如ansi,可以调用QString::fromLocal8bit一、字面量的编码取决于文件,即如果在c++源文件中有直接赋值1)、constchar*s="......
  • 22. 括号生成-c++
    数字n代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。示例1:输入:n=3输出:["((()))","(()())","(())()","()(())","()()()"]示例2:输入:n=1输出:["()"]classSolution{public:vector<string>generat......
  • 39. 组合总和-c++
    给你一个无重复元素的整数数组candidates和一个目标整数target,找出candidates中可以使数字和为目标数target的所有不同组合,并以列表形式返回。你可以按任意顺序返回这些组合。candidates中的同一个数字可以无限制重复被选取。如果至少一个数字的被选数量不同......
  • C++内存分布
    内存分布典型的C/C++内存表示有以下几部分构成Textsegment,存放代码段和只读常量的区域Initializeddatasegment,通常叫做数据段,已经初始化的静态变量和全局变量存放的区域,如staticinti=5、全局变量intj=10都存放在数据段Uninitializeddatasegment(bss),未初始化的静......
  • 78. 子集-c++
    给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。你可以按任意顺序返回解集。示例1:输入:nums=[1,2,3]输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]示例2:输入:nums=[0]输出:[[],[0]]classSolution{......
  • c++
    namespace命名空间我们为了防止由于定义了多个函数名,导致可能函数名重复的问题,我们创建了命名空间,为的就是区分函数名重复的问题当我们要调用某个命名空间中的函数时,我们可以直接namespace::function来进行直接调用,同时可以存放变量名,函数,再一个命令空间.....同时假如命名空间......
  • C++学习笔记
    参考https://github.com/weidongshan/cpp_projects《C++PrimerPlus》C++StandardsSupportinGCCGCCGCC中有libstdc++库的实现LLVMLLVM中有libc++库的实现面向对象编程的3大特点封装继承多态struct声明的类里的成员都是publicclass声明的类的成员都是pr......
  • 解决报错:Could not set property 'id' of 'class com.north.domain.Book' with value
    报错原因问题描述:因为MyBatis-Plus默认的id自增策略使用的雪花算法org.mybatis.spring.MyBatisSystemException:nestedexceptionisorg.apache.ibatis.reflection.ReflectionException:Couldnotsetproperty'id'of'classcom.north.domain.Book'withvalue'1......