C++ 面向对象(一)—— 类(Classes)

 类(class)是一种将数据和函数组织在同一个结构里的逻辑方法。定义类的关键字为class ,其功能与C语言中的struct类似,不同之处是class可以包含函数,而不像struct只能包含数据元素。

类定义的形式是:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class class_name {  
  2.     permission_label_1:  
  3.         member1;  
  4.     permission_label_2:  
  5.         member2;  
  6.     ...  
  7. } object_name;  

      其中 class_name 是类的名称 (用户自定义的类型) ,而可选项object_name 是一个或几个对象(object)标识。Class的声明体中包含成员members,成员可以是数据或函数定义,同时也可以包括允许范围标志 permission labels,范围标志可以是以下三个关键字中任意一个:private:, public: 或 protected:。它们分别代表以下含义:

  • private :class的private成员,只有同一个class的其他成员或该class的“friend” class可以访问这些成员。
  • protected :class的protected成员,只有同一个class的其他成员,或该class的“friend” class,或该class的子类(derived classes) 可以访问这些成员。
  • public :class的public成员,任何可以看到这个class的地方都可以访问这些成员。

如果我们在定义一个class成员的时候没有声明其允许范围,这些成员将被默认为 private范围。

例如:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class CRectangle {  
  2.   
  3.            int x, y;  
  4.        public:  
  5.            void set_values (int,int);  
  6.            int area (void);  
  7.    } rect;  
  8.      

      上面例子定义了一个class CRectangle 和该class类型的对象变量rect 。这个class 有4个成员:两个整型变量 (x 和 y) ,在private 部分 (因为private 是默认的允许范围);以及两个函数, 在 public 部分:set_values() 和 area(),这里只包含了函数的原型(prototype)。

      注意: class名称与对象(object)名称的不同:在上面的例子中,CRectangle 是class 名称 (即用户定义的类型名称),而rect 是一个CRectangle类型的对象名称。它们的区别就像下面例子中类型名 int和 变量名a 的区别一样:

int a;

int 是class名称 (类型名) ,而a 是对象名 object name (变量)。

    在程序中,我们可以通过使用对象名后面加一点再加成员名称(同使用C structs一样),来引用对象rect 的任何public成员,就像它们只是一般的函数或变量。例如:

rect.set_value (3,4);
myarea = rect.area();

但我们不能够引用 x 或 y ,因为它们是该class的 private 成员,它们只能够在该class的其它成员中被引用。晕了吗?下面是关于class CRectangle的一个复杂的例子:

    // classes example#include <iostream.h>class CRectangle {int x, y;public:void set_values (int,int);int area (void) {return (x*y);}};void CRectangle::set_values (int a, int b) {x = a;y = b;}int main () {CRectangle rect;rect.set_values (3,4);cout << "area: " << rect.area();}			
area: 12

     上面代码中新的东西是在定义函数set_values().使用的范围操作符(双冒号:: )。它是用来在一个class之外定义该class的成员。注意,我们在CRectangle class内部已经定义了函数area() 的具体操作,因为这个函数非常简单。而对函数set_values() ,在class内部只是定义了它的原型prototype,而其实现是在class之外定义的。这种在class之外定义其成员的情况必须使用范围操作符::。

     范围操作符 (::) 声明了被定义的成员所属的class名称,并赋予被定义成员适当的范围属性,这些范围属性与在class内部定义成员的属性是一样的。例如,在上面的例子中,我们在函数set_values() 中引用了private变量x 和 y,这些变量只有在class内部和它的成员中才是可见的。

    在class内部直接定义完整的函数,和只定义函数的原型而把具体实现放在class外部的唯一区别在于,在第一种情况中,编译器(compiler) 会自动将函数作为inline 考虑,而在第二种情况下,函数只是一般的class成员函数。

    我们把 x 和 y 定义为private 成员 (记住,如果没有特殊声明,所有class的成员均默认为private ),原因是我们已经定义了一个设置这些变量值的函数 (set_values()) ,这样一来,在程序的其它地方就没有办法直接访问它们。也许在一个这样简单的例子中,你无法看到这样保护两个变量有什么意义,但在比较复杂的程序中,这是非常重要的,因为它使得变量不会被意外修改 (这里意外指的是从object的角度来讲的意外)。

   使用class的一个更大的好处是我们可以用它来定义多个不同对象(object)。例如,接着上面class CRectangle的例子,除了对象rect之外,我们还可以定义对象rectb :

    // class example#include <iostream.h>class CRectangle {int x, y;public:void set_values (int,int);int area (void) {return (x*y);}};void CRectangle::set_values (int a, int b) {x = a;y = b;}int main () {CRectangle rect, rectb;rect.set_values (3,4);rectb.set_values (5,6);cout << "rect area: " << rect.area() << endl;cout << "rectb area: " << rectb.area() << endl;}
rect area: 12
rectb area: 30

注意: 调用函数rect.area() 与调用rectb.area()所得到的结果是不一样的。这是因为每一个class CRectangle 的对象都拥有它自己的变量 x 和 y,以及它自己的函数set_value() 和 area()。

        这是基于对象( object) 和 面向对象编程 (object-oriented programming)的概念的。这个概念中,数据和函数是对象(object)的属性(properties),而不是像以前在结构化编程 (structured programming) 中所认为的对象(object)是函数参数。在本节及后面的小节中,我们将讨论面向对象编程的好处。

在这个具体的例子中,我们讨论的class (object的类型)是CRectangle,有两个实例(instance),或称对象(object):rect 和 rectb,每一个有它自己的成员变量和成员函数。


构造函数和析构函数 (Constructors and destructors)

       对象(object)在生成过程中通常需要初始化变量或分配动态内存,以便我们能够操作,或防止在执行过程中返回意外结果。例如,在前面的例子中,如果我们在调用函数set_values( ) 之前就调用了函数area(),将会产生什么样的结果呢?可能会是一个不确定的值,因为成员x 和 y 还没有被赋于任何值。

      为了避免这种情况发生,一个class 可以包含一个特殊的函数:构造函数 constructor,它可以通过声明一个与class同名的函数来定义。当且仅当要生成一个class的新的实例 (instance)的时候,也就是当且仅当声明一个新的对象,或给该class的一个对象分配内存的时候,这个构造函数将自动被调用。下面,我们将实现包含一个构造函数的CRectangle :

    // class example#include <iostream.h>class CRectangle {int width, height;public:CRectangle (int,int);int area (void) {return (width*height);}};CRectangle::CRectangle (int a, int b) {width = a;height = b;}int main () {CRectangle rect (3,4);CRectangle rectb (5,6);cout << "rect area: " << rect.area() << endl;cout << "rectb area: " << rectb.area() << endl;}    
rect area: 12
rectb area: 30

      正如你所看到的,这个例子的输出结果与前面一个没有区别。在这个例子中,我们只是把函数set_values换成了class的构造函数constructor。注意这里参数是如何在class实例 (instance)生成的时候传递给构造函数的:

CRectangle rect (3,4);
CRectangle rectb (5,6);

     同时你可以看到,构造函数的原型和实现中都没有返回值(return value),也没有void 类型声明。构造函数必须这样写。一个构造函数永远没有返回值,也不用声明void,就像我们在前面的例子中看到的。


      析构函数Destructor 完成相反的功能。它在objects被从内存中释放的时候被自动调用。释放可能是因为它存在的范围已经结束了(例如,如果object被定义为一个函数内的本地(local)对象变量,而该函数结束了);或者是因为它是一个动态分配的对象,而被使用操作符delete释放了。

     析构函数必须与class同名,加水波号tilde (~) 前缀,必须无返回值

     析构函数特别适用于当一个对象被动态分别内存空间,而在对象被销毁的时我们希望释放它所占用的空间的时候。例如:

    // example on constructors and destructors#include <iostream.h>class CRectangle {int *width, *height;public:CRectangle (int,int);~CRectangle ();int area (void) {return (*width * *height);}};CRectangle::CRectangle (int a, int b) {width = new int;height = new int;*width = a;*height = b;}CRectangle::~CRectangle () {delete width;delete height;}int main () {CRectangle rect (3,4), rectb (5,6);cout << "rect area: " << rect.area() << endl;cout << "rectb area: " << rectb.area() << endl;return 0;}
rect area: 12
rectb area: 30



构造函数重载(Overloading Constructors)

      像其它函数一样,一个构造函数也可以被多次重载(overload)为同样名字的函数,但有不同的参数类型和个数。记住,编译器会调用与在调用时刻要求的参数类型和个数一样的那个函数(Section 2.3, Functions-II)。在这里则是调用与类对象被声明时一样的那个构造函数。

      实际上,当我们定义一个class而没有明确定义构造函数的时候,编译器会自动假设两个重载的构造函数 (默认构造函数"default constructor" 和复制构造函数"copy constructor")。例如,对以下class:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. class CExample {  
  2.   public:  
  3.     int a,b,c;  
  4.     void multiply (int n, int m) { a=n; b=m; c=a*b; };  
  5. };  

没有定义构造函数,编译器自动假设它有以下constructor 成员函数:

  • Empty constructor

    它是一个没有任何参数的构造函数,被定义为nop (没有语句)。它什么都不做。

    CExample::CExample () { };
  • Copy constructor

    它是一个只有一个参数的构造函数,该参数是这个class的一个对象,这个函数的功能是将被传入的对象(object)的所有非静态(non-static)成员变量的值都复制给自身这个object。

    [cpp] view plaincopy
    在CODE上查看代码片派生到我的代码片
    1. CExample::CExample (const CExample& rv) {  
    2.      a=rv.a;  b=rv.b;  c=rv.c;  
    3.  }  
    4.    

必须注意:这两个默认构造函数(empty construction 和 copy constructor )只有在没有其它构造函数被明确定义的情况下才存在。如果任何其它有任意参数的构造函数被定义了,这两个构造函数就都不存在了。在这种情况下,如果你想要有empty construction 和 copy constructor ,就必需要自己定义它们。

       当然,如果你也可以重载class的构造函数,定义有不同的参数或完全没有参数的构造函数,见如下例子:

    // overloading class constructors#include <iostream.h>Class CRectangle {int width, height;public:CRectangle ();CRectangle (int,int);int area (void) {return (width*height);}};CRectangle::CRectangle () {width = 5;height = 5;}CRectangle::CRectangle (int a, int b) {width = a;height = b;}int main () {CRectangle rect (3,4);CRectangle rectb;cout << "rect area: " << rect.area() << endl;cout << "rectb area: " << rectb.area() << endl;}
rect area: 12
rectb area: 25

     在上面的例子中,rectb 被声明的时候没有参数,所以它被使用没有参数的构造函数进行初始化,也就是width 和height 都被赋值为5。

注意在我们声明一个新的object的时候,如果不想传入参数,则不需要写括号():

CRectangle rectb; // right
CRectangle rectb(); // wrong! 



类的指针(Pointers to classes)

      类也是可以有指针的,要定义类的指针,我们只需要认识到,类一旦被定义就成为一种有效的数据类型,因此只需要用类的名字作为指针的名字就可以了。例如:

CRectangle * prect;

      是一个指向class CRectangle类型的对象的指针。

      就像数据机构中的情况一样,要想直接引用一个由指针指向的对象(object)中的成员,需要使用操作符 ->。这里是一个例子,显示了几种可能出现的情况:

    // pointer to classes example#include <iostream.h>class CRectangle {int width, height;public:void set_values (int, int);int area (void) {return (width * height);}};void CRectangle::set_values (int a, int b) {width = a;height = b;}int main () {CRectangle a, *b, *c;CRectangle * d = new CRectangle[2];b= new CRectangle;c= &a;a.set_values (1,2);b->set_values (3,4);d->set_values (5,6);d[1].set_values (7,8);cout << "a area: " << a.area() << endl;cout << "*b area: " << b->area() << endl;cout << "*c area: " << c->area() << endl;cout << "d[0] area: " << d[0].area() << endl;cout << "d[1] area: " << d[1].area() << endl;return 0;}
a area: 2
*b area: 12
*c area: 2
d[0] area: 30
d[1] area: 56

以下是怎样读前面例子中出现的一些指针和类操作符 (*, &, ., ->, [ ]):

  • *x 读作: pointed by x (由x指向的)
  • &x 读作: address of x(x的地址)
  • x.y 读作: member y of object x (对象x的成员y)
  • (*x).y 读作: member y of object pointed by x(由x指向的对象的成员y)
  • x->y 读作: member y of object pointed by x (同上一个等价)
  • x[0] 读作: first object pointed by x(由x指向的第一个对象)
  • x[1] 读作: second object pointed by x(由x指向的第二个对象)
  • x[n] 读作: (n+1)th object pointed by x(由x指向的第n+1个对象)

在继续向下阅读之前,一定要确定你明白所有这些的逻辑含义。如果你还有疑问,再读一遍这一笑节,或者同时参考 小节 "3.3, 指针(Pointers)" 和 "3.5, 数据结构(Structures)".


由关键字struct和union定义的类

        类不仅可以用关键字class来定义,也可以用struct或union来定义。

        因为在C++中类和数据结构的概念太相似了,所以这两个关键字struct和class的作用几乎是一样的(也就是说在C++中struct定义的类也可以有成员函数,而不仅仅有数据成员)。两者定义的类的唯一区别在于由class定义的类所有成员的默认访问权限为private,而struct定义的类所有成员默认访问权限为public。除此之外,两个关键字的作用是相同的。

       union的概念与struct和class定义的类不同, 因为union在同一时间只能存储一个数据成员。但是由union定义的类也是可以有成员函数的。union定义的类访问权限默认为public。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401829.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Seen.js – 使用 SVG 或者 Canvas 渲染 3D 场景

Seen.js 渲染3D场景为 SVG 或者 HTML5 画布。Seen.js 包含对于 SVG 和 HTML5 Canvas 元素的图形功能的最简单的抽象。所有这个库的其它组件都是不用关心将要渲染的上下文的类型。 您可能感兴趣的相关文章你见过吗&#xff1f;9款超炫的复选框&#xff08;Checkbox&#xff09;效…

C++ 面向对象(三)—— 类之间的关系

友元函数(Friend functions) 在前面的章节中我们已经看到了对class的不同成员存在3个层次的内部保护&#xff1a;public&#xff0c; protected 和 private。在成员为 protected 和 private的情况下&#xff0c;它们不能够被从所在的class以外的部分引用。然而&#xff0c;这个…

五年后存储会是什么样子

原文&#xff1a;http://chucksblog.emc.com/chucks_blog/2013/06/what-storage-might-look-like-in-five-years.html注明&#xff1a;本文内容基于 VMware VSAN beta 版本撰写&#xff0c;请访问http://www.vmware.com/products/virtual-san/获得有关正式版本的更新信息。有时…

java.lang.NoClassDefFoundError: javax/transaction/Synchronization (jUnit测试报错)

测试hibernate报错原因项目缺少包在 hibernate 解压目录下找到 jta.jar 文件往项目中添加该 jar 包&#xff0c;即可解决添加方法&#xff1a;【右击项目】-->【构建路径】....来自为知笔记(Wiz)转载于:https://www.cnblogs.com/zhanyao/p/3711322.html

yarn oom问题一例

线上部分job运行失败&#xff0c;报OOM的错误:因为是maptask报错&#xff0c;怀疑是map数量过少&#xff0c;导致oom&#xff0c;因此调整参数&#xff0c;增加map数量&#xff0c;但是问题依然存在。看来和map的数量没有关系。通过jobid查找jobhistory中对应的日志信息&#x…

C++ 高级篇(三)—— 出错处理

本节介绍的出错处理是ANSI-C 标准引入的新功能。如果你使用的C 编译器不兼容这个标准&#xff0c;则你可能无法使用这些功能。 在编程过程中&#xff0c;很多时候我们是无法确定一段代码是否总是能够正常工作的&#xff0c;或者因为程序访问了并不存在的资源&#xff0c;或者由…

C++ 高级篇(一)—— 模板(Templates)

模板(Templates)是ANSI-C 标准中新引入的概念。如果你使用的 C 编译器不符合这个标准&#xff0c;则你很可能不能使用模板。 函数模板( Function templates) 模板(Templates)使得我们可以生成通用的函数&#xff0c;这些函数能够接受任意数据类型的参数&#xff0c;可返回任意类…

PPT到底是天使还是魔鬼?

说老实话&#xff0c;我非常不喜欢那些PPT的培训者一味地鼓吹PPT的重要性&#xff0c;视觉化思维的重要性&#xff0c;PPT在演讲中的重要性等等&#xff0c;我很清楚他们这么说是因为他们必须这么说&#xff0c;因为如果不把PPT的重要性强调一下&#xff0c;谁来上他的课呢&…

ultraedit正则表达式

一般使用ultraedit中的Perl风格的正则表达式&#xff0c;下面是perl正则的基本语法 perl中的元字符如下&#xff1a; ^ 表示一行的开头&#xff1b; $ 表示一行的结尾&#xff1b; ( ) 表示一个匹配块的&#xff0c;可以对匹配上的块通过$1,$2...进行读取&#xff0c;…

微软云介绍

微软云介绍 http://msdn.microsoft.com/zh-cn/ff380142 什么是云开发&#xff1f;&#xff08;概述&#xff09; 云计算是指远程运行并通过 Internet 访问的计算机和应用程序。在云计算中&#xff0c;虚拟机在大型数据中心中运行&#xff0c;并取代了物理 PC 和服务器。通过将许…

IO端口和IO内存的区别及分别使用的函数接口

IO端口和IO内存的区别及分别使用的函数接口 每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口&#xff0c;通常包括&#xff1a;控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式&#xff0c;可以把CPU分成两大类。一类CPU&#xff08…

Win8 HTML5与JS编程学习笔记(二)

近期一直受到win8应用的Grid布局困扰&#xff0c;经过了半下午加半个晚上的奋斗&#xff0c;终于是弄明白了Grid布局方法的规则。之前我是阅读的微软官方的开发教程&#xff0c;书中没有详细说明CSS3的布局规则&#xff0c;自己鼓捣了半天也是一头雾水&#xff0c;于是又找到了…

Windows下的Qt Creator的安装

采用Qt和Qt creator分别下载和安装的方式&#xff1a;&#xff08;需要手动设置关联Qt和Qt Creator&#xff09; 一、软件下载 从http://qt-project.org/downloads分别下载Qt和Qt Creator&#xff1a; Qt使用4.7.2版本&#xff1a;qt-win-opensource-4.7.2-mingw.exe Qt Creato…

进程 、进程组、会话、控制终端之间的关系

一个进程组可以包含多个进程 进程组中的这些进程之间不是孤立的&#xff0c;他们彼此之间或者存在者父子、兄弟关系&#xff0c;或者在功能有相近的联系。 那linux为什么要有进程组呢&#xff1f;其实提供进程组就是方便管理这些进程。假设要完成一个任务&#xff0c;需要同时并…

Angularjs 通过asp.net web api认证登录

Angularjs 通过asp.net web api认证登录 Angularjs利用asp.net mvc提供的asp.net identity&#xff0c;membership实现居于数据库的用户名/密码的认证登录 环境 Vs.net 2013 Asp.net mvc web api Individual user accounts Angularjs Underscore 新建一个asp.net mvc web api …

PANIC: Unreachable code reached.

为什么80%的码农都做不了架构师&#xff1f;>>> Caused by: java.lang.RuntimeException: PANIC: Unreachable code reached.at cryptix.jce.provider.cipher.BlockCipher.engineGetParameters(BlockCipher.java:244)at javax.crypto.Cipher.checkCryptoPerm(Ciphe…

[LeetCode Online Judge]系列-求二维平面内在一条直线上的最大点数

2019独角兽企业重金招聘Python工程师标准>>> Max Points on a Line Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. 题目是:在2D平面内给N个点,求最多多少个点在一个直线上. 以下是AC的解决方式: /*** Defi…

【思科】GNS3模拟静态NAT/动态NAT

实验拓扑&#xff1a;实验目的&#xff1a;利用NAT技术&#xff0c;使C1,C2能与R2连通。实验步骤&#xff1a;如图配置好IP地址&#xff0c;为C1,C2指定网关192.168.1.1。2.在R1上配置静态NAT.假定为C1配置公网地址12.0.0.3&#xff0c;来实现与R2的连通。这样&#xff0c;静态…

xml 解析

2019独角兽企业重金招聘Python工程师标准>>> 各大数据接口大全&#xff1a;http://blog.sina.com.cn/s/articlelist_2127818045_10_1.html package com.test.junit; import static org.junit.Assert.*; import java.io.ByteArrayInputStream;import java.io.InputSt…

理解Lucene/Solr的缓存

缓存对于提高搜索引擎的吞吐量&#xff0c;降低CPU占用率极为重要。Lucene/Solr在这块做了很多的工作。Lucene/Solr中默认提供了5种缓存&#xff0c;同时solr还提供扩展缓存接口&#xff0c;允许开发者自定义缓存。1 缓存的基本原理Solr实现了两种策略的缓存&#xff1a;LRU(Le…