嗯,上一篇已经介绍了面向过程编程的语法知识,接下来是最后的也是最重要的一个部分:

第三部分:基于对象的编程风格

1.构造函数的两种写法

比如我们有如下的类定义:

class Circle

{

public:

  Circle(float r);

private:

  float radius;

};

上面我们只是声明了构造函数,接下来要具体定义它。第一种写法很常见,也好理解,大家通常都是这么写的:

Circle::Circle(float r) { radius = r }

但是除了在函数体内部对类成员赋值的写法外,还有一种写法:类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表)... { 类的初始化 }。而且据说使用初始化列表比使用赋值语句的效率要高。 于是上面的构造函数可以写成如下形式:

Circle::Circle(float r):radius(r){}

恩,其实我还是比较喜欢第一种,然而有时候却必须用第二种:类成员有const(因为const成员只能被初始化,不能被赋值)和引用数据成员只能用初始化写法(第二种),不能被赋值写法(第一种)。

2.构造函数的重载

在一个类中可以定义多个构造函数,以便提供不同的初始化的方法,供用户选用。这些构造函数具有相同的名字,而参数的个数或参数的类型不相同。这称为构造函数的重载。 接下来看一个例子吧:

class Box

{

public :

  //定义一个无参的构造函数

  Box(){ height=10; width=10; length=10; };

  //定义一个有参的构造函数,用参数的初始化表对数据成员初始化

  Box(int h,int w,int len):height(h),width(w),length(len){ }

private :

  int height;  //高度

  int width;   //宽度

  int length;  //长度

}

好吧,这个例子是我网上找来的,而且我个人认为这是错误的示范,绝对的错误,这根本不是重载的正确用法,我觉得这种情况应该用默认参数技术!!那么什么时候才该使用构造函数的重载呢,你可以看看“C++ 我想这样用(五) ”中的重载例子。那个例子里的类才是真正需要重载构造函数的。

3.要不要析构函数

析构函数是与构造函数作用相反的函数。析构函数在下边3种情况时被调用:

a.对象生命周期结束,被销毁时;

b.delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;

c.对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。

析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。

通常如果你的类成员都是一些“值类型”,换言之你的类成员都是“储值”的类型,如int char等,生存期过了就会被回收,那么你不需要写析构函数。反之如果你的类成员中有“储地址”的类型,比如指向new出来的空间的指针,那么你可一定要记得写析构函数,而且妥当的delete掉你new出来的空间。否则就引起了内存泄漏了。

class MyClass

{

private:

  char * _pName;

public:

  //这是你的构造函数

  MyClass(const char *name)

  {

    if (name)  {   _pName=new char[strlen(name)];  ......     };

  };

  //这时需要自己写出析构函数

~MyClass()

  {

    if (_pName)            delete [] pName;

  };

};

4.类的复制及四个默认函数

有人说,我没有写构造和析构函数啊,为甚还是可以正常的创建和销毁一个对象啊?嗯,好吧,我只能说编译器再一次给你擦屁股了。。。。。。 其实空类什么都不会有,所谓的默认构造函数、默认复制(拷贝)构造函数、默认赋值符操作符、默认析构函数是不会产生的,只有当用到这些函数或操作符时,编译器才会为我们产生所需要的函数或操作符。但是编译器产生的是什么特殊功能都没有的函数,为的只是让我们的程序能编译通过,如果你想要自己实现什么功能,如上面说的构造和析构,请自己实现吧~~~

构造和析构已经说过了,然而类中还有一个比较常用的功能就是复制,下面就说说和“复制”相关的拷贝构造函数和赋值符函数吧:

Triangular tri1(8);

Triangular tri2 = tri1;  //写法一

Triangular tri2(tri1);   //写法二

上述两种写法的形式虽然不同,但是意义是一样的,都是用已经存在的对象来创建新的对象,使两者内容一致。类不是int,怎么说复制就复制了呢?哪有那么容易呢?没错,我们能这样做是因为编译器为我们生成了默认的拷贝构造函数:

Triangular::Triangular(const Triangular& tri);

该默认函数是以成员逐一初始化的方式执行的,即类成员逐个复制。然而这种方法显然并不适合所有情况,如果我们的类成员有指针型的呢?没错,我们就只复制了地址,没有复制对应的空间,我们通常想要的是完整的拷贝,拷贝之后的tri1和tri2应该是互相无关的。这时我们需要自己来完成拷贝构造函数的编写。注意拷贝构造函数是参数唯一且固定的构造函数,所以没有返回值。在C++语言里:String   s2(s1);  和  String   s3   =   s1; 只是语法形式的不同,意义是一样的,都是定义加初始化,都调用拷贝构造函数。

除了上面这种完整的彻底的复制(深复制),我们有时仅仅需要简单的引用,即浅复制:

Triangular tri1(8);

Triangular tri2 ;

tri2 = tri1;   //这里完全不同与上面的写法一

这种情况下,我们已经有了两个存在了的对象,要做的仅仅是让tri2简单的与tri1有相同的引用。这时我们用到了赋值符函数:

Triangular & Triangular::operator = (const Triangular& RightSides)
{
    nSize=RightSides.nSize; //复制常规成员
    char *temp=new char[nSize]; //复制指针指向的内容 
    memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));
    delete []pBuffer; //删除原指针指向内容  (将删除操作放在后面,避免X=X特殊情况下,内容的丢失)
    pBuffer=temp;   //建立新指向
    return *this
}

和拷贝构造函数的实现不一样,operator=();是把一个对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。什么????你说没看懂上面的像函数的东西是个啥??好吧,我也一样看不懂,因为这已经涉及到操作符重载了,是很高深的哦,后面会说到。总之在这部分要记住三个比较关键的函数:构造函数,析构函数,以及拷贝构造函数。这不是C++所特有的东西,是面向对象的基本概念,一般的面向对象语言里都有。比如java和C#,只不过语法上大同小异而已。

5.类的静态成员和静态成员函数

声明为static的类成员或者成员函数便能在类的范围内共同享,我们把这样的成员称做静态成员和静态成员函数。静态方法就是与该类相关的,是类的一种行为,而不是与该类的实例对象相关。静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。同样静态成员可以被初始化,但也只能在类体外进行初始化。我在网上找来了一个例子,比较直观:

#include <iostream> 
using namespace std; 
 
class Internet 

public: 
    Internet(char *name,char *address) 
    { 
        strcpy(Internet::name,name); 
        strcpy(Internet::address,address); 
        count++; 
    } 
    static void Internet::Sc()//静态成员函数 
    { 
        cout<<count<<endl; 
    } 
    Internet &Rq(); 
public: 
    char name[20]; 
    char address[20]; 
    static int count;//这里如果写成static int count=0;就是错误的 
}; 
 
Internet& Internet::Rq()//返回引用的成员函数 

    return *this; 

 
int Internet::count = 0;//静态成员的初始化 
void vist() 

    Internet a1("中国软件开发实验室","www.cndev-lab.com"); 
    Internet a2("中国软件开发实验室","www.cndev-lab.com"); 

void fn(Internet &s) 

    cout<<s.Rq().count; 

void main() 

    cout<<Internet::count<<endl;//静态成员值的输出 
    vist(); 
    Internet::Sc();//静态成员函数的调用 
    Internet b("中国软件开发实验室","www.cndev-lab.com"); 
    Internet::Sc(); 
    fn(b); 
    cin.get(); 
}

一定要理解了静态成员或静态成员函数的实际意义,才去使用它们,不然就是毫无意义的了。这里我吐槽下python,一般的语言都是这种定义,静态的类方法和非静态的类方法,可是python里面却有三种东西:实例函数,类函数,静态函数。第一个很好理解,就是那种普通的“类的非静态函数”了,但是后面两个真的就有些暧昧不清了,我是有些糊涂的,是的,曾经搞清楚了,因为我不会3个同时用到,所以很快又混淆了,不知道哪位大大解释下这种特立独行的意义。

C++ 我想这样用(六)的更多相关文章

  1. 初探JAVA中I/O流(二)

    1.缓冲输入文件 FileReader BufferedReader FileReader可以直接对文件进行读操作.但是简化编程,加快读取速度,我们加入了缓冲机制,使用了BufferedReader. ...

  2. centos启动流程[转]

    启动流程概览 在硬件驱动成功后,Kernel 会主动呼叫 init 程序,而 init 会取得 run-level 资讯: init 运行 /etc/rc.d/rc.sysinit 文件来准备软件运行 ...

  3. 【转】linux-系统启动流程详解

    第二十章.启动流程.模块管理与 Loader 最近升级日期:2009/09/14 1. Linux 的启动流程分析 1.1 启动流程一览 1.2 BIOS, boot loader 与 kernel ...

  4. SVN--VisualSVN server 服务端和 TortoiseSVN客户端的基础使用

    前言 在上一文http://www.cnblogs.com/wql025/p/5177699.html中,我们讲到了使用SVN的第一步,即下载.安装SVN的服务端软件--VisualSVN serve ...

  5. 关于win7系统中所有exe文件都被以word方式打开的解决方法

    手残一刻,电脑桌面所有的软件快捷方式都变成了word的打开方式,鼠标右键选中某exe文件也没打开方式那个选项, 第一次尝试: 在控制面板——默认程序中修改默认打开方式,但是没有找到解决方法

  6. 银行爱“IOE”爱得有多深

    本文由阿尔法工场欧阳长征推荐 导读:如果银行是一家海鲜酒楼,把IBM换掉相当于大搞一次装修,把Oracle换掉相当于把厨子和菜谱全部换掉,把EMC换掉相当于把放食材工具的储物间换个地方.难度在于,这海 ...

  7. Introduction to REST #Reprinted#

    from http://www.cnblogs.com/shanyou/archive/2012/05/12/2496959.html dudu的 HttpClient + ASP.NET Web A ...

  8. 摘录<小王子>——[法]安东·圣埃克苏佩里

    四 大人们都喜欢数字.你要是向他们说起一个新朋友,他们提出的问题从来问不到点子上. 他们绝不会问:"他的嗓音怎么样?他喜欢什么游戏?比如,他喜欢搜集蝴蝶标本吗?" 他们总是问你:& ...

  9. [Tensorflow实战Google深度学习框架]笔记4

    本系列为Tensorflow实战Google深度学习框架知识笔记,仅为博主看书过程中觉得较为重要的知识点,简单摘要下来,内容较为零散,请见谅. 2017-11-06 [第五章] MNIST数字识别问题 ...

  10. RESTful Web服务的操作

    1.首先我们说一下Http协议是无状态的 HTTP协议是无状态的,我们看到查到的用到的返回404,500,200,201,202,301.这些不是HTTP协议的状态码. 是HTTP的状态码,就是HTT ...

随机推荐

  1. SQLite数据库的体系结构(翻译自sqlite.org)

    $1 简介    本文档描述了SQLite库的体系结构,这些信息对那些想理解和修改SQLite的内部工作机制的人是有用的.    下图显示了SQLite的主要组成部件及其相互关系,下面的内容将描述每一 ...

  2. Loongnix 系统(MIPS Linux)

    电脑上的x86,手机上的ARM,在各自领域都是很成熟的CPU架构了,龙芯也参与进去竞争是很难的,就算是Intel,挤破头皮疯狂补贴自家的Atom x86还是在手机领域无法立足. 所以说,个人觉得龙芯可 ...

  3. VC程序查错之内存访问异常

    作者:langouster 先来看下面这张图,相信很多程序员都见过类似. ---------------------------test1.exe - 应用程序错误------------------ ...

  4. latex 三线表

    LaTeX 处理三线表相当简单方便.用到的宏包主要是 booktabs .代码如下: 需要添加包:\usepackage{booktabs}. \documentclass{article} \use ...

  5. ehcache版本冲突

    以ehchache-core2.5为分水岭 缓存版本问题 版本不一样 配置不一样  ehcache-core-2.4.3.jar 与 ehcache-core-2.6.6 一 Caused by: n ...

  6. Mybatis中配置Mapper的方法

    在这篇文章中我主要想讲一下Mybatis配置文件中mappers元素的配置.关于基础部分的内容可以参考http://haohaoxuexi.iteye.com/blog/1333271. 我们知道在M ...

  7. 【转载】Java垃圾回收内存清理相关(虚拟机书第三章),GC日志的理解,CPU时间、墙钟时间的介绍

    主要看<深入理解Java虚拟机> 第三张 P84 开始是垃圾收集相关. 1. 1960年诞生于MIT的Lisp是第一门采用垃圾回收的语言. 2. 程序计数器.虚拟机栈.本地方法栈3个区域随 ...

  8. [转载]Python模块学习 ---- subprocess 创建子进程

    [转自]http://blog.sciencenet.cn/blog-600900-499638.html 最近,我们老大要我写一个守护者程序,对服务器进程进行守护.如果服务器不幸挂掉了,守护者能即时 ...

  9. SGU 275 To xor or not to xor (高斯消元)

    题目链接 题意:有n个数,范围是[0, 10^18],n最大为100,找出若干个数使它们异或的值最大并输出这个最大值. 分析: 一道高斯消元的好题/ 我们把每个数用二进制表示,要使得最后的异或值最大, ...

  10. zoj 3785 What day is that day? (打表找规律)

    题目 思路:比赛的时候有想过找循环节,但是,打表打错了. 后来,看着过了挺多人,就急了, 看了一下别人的时间 耗时都挺长的,就以为不是找规律, 没想到真是找规律,不过,这个题的数据可能挺大的. AC代 ...