《VC++深入详解–学习笔记》(2)掌握C++

Filed Under (VC++ 学习笔记) by panmaoru on 01-12-2009

题外话

打开掌握C++这一章节,首先想到的是以前我非常喜欢的一个英语教师老罗说的一句话:一个人如果连自己的母语都学不好,那他很难学好一门外语。接触程序开发差不多有4,5年的时间了,现在看来,我是那个没有学好母语的人。

大学期间学的第一门程序设计语言是C++,成果可以用惨烈来形容,课程结束之后留下的印象大概就是:指针太繁琐了,C++博大精深啊!再后来便喜欢上了一种叫做asp.net的开发技术,因为它其中使用的C#语言是没有指针的。很不幸的是做asp.net开发这几年我只是粗略的翻过《C#入门经典-第三版》和《C#高级编程-第四版》,至于程序员必备的MSDN,我基本上都没安装过,罢了,从零开始,学习C++。

看《VC++深入详解》这本书之前,每天大概2个小时,花了半个月的把《C++ Primer-第四版》看了一遍,因为实际动手较少,只能说有一个初略的了解,对C就完全没有概念了,只能猜测下C和C++在基本的表达式,语句和控制流程等基础语法方面是类同的。

面向对象

C++是面向对象的程序设计语言,带类的C,比C多出了面向对象的特性,“面向对象”这个概念够大了,面向对象的主要特点有

  •  封装性:把数据和操作数据的函数组织在一起,不仅使程序更加紧凑,而且提高了类的内部数据的安全性;
  • 继承性:是一个类具有另外一个类的属性(数据)和行为(函数),增加了程序的可拓展性和代码的复用;
  • 多态性:一个接口,不同的实现(很难理解,很难解释)。

结构和类

写一个简单的C++程序:

//看完书本中的第一张,VC中很多结构的定义,烦

#include <iostream.h>

struct point//换成class

{

       int x;

       int y;

};

void main()

{

       point pt;

       pt.x=10;

       pt.y=12;

       cout<<"x="<<pt.x<<"y="<<pt.y<<endl;

}

程序运行,结果是正常的,输出赋值的x和y的值。

如果把struct换成class,用类来定义就会有问题,这里是由于访问权限的原因struct的数据成员默认是public的,class默认是private。刚开始不明白,后来想想,大概是类的封装特性吧,为了保护类的数据和类的成员函数。

类和对象

看过几本C++的书,关于类和对象的说明大多很繁琐,过于的术语话,直到后来看到一篇“面向对象思想,不看后悔“的网文才算对类和对象有了稍微清晰的了解。摘一小段过来,重温下。

面向对象的编程语言最大的特色就是可以编写自己所需的数据类型,以更好的解决问题。我想我必须要帮你搞清楚“类,对象,属性,方法它们之间的关 系”!就像我前面所说的,人这个“类”是什么也做不了的,因为“人类”只是一个抽象的概念,它不是实实在在的“东西”,而这个“东西”就是所谓的对象。只 有人这个“对象”才能去工作。而类呢?类是对象的描述!对象从类中产生出来!此时,对象具有类所描述的所有的属性以及方法。
也许你已经有些不知所措了,没关系!好好的回味一下,我再举个例子!例如电视机,电视机都有工作原理图,那么什么叫电视机呢?只要它能够实现工作原理图的 所有功能的物体,我们都叫它电视机。你想想是不是这么一回事儿?可是,电视机原理图是不能工作的,也就是这个原理图不能收看节目,只有电视机这个“实体 ——即所谓的对象”才能收看节目,也就是说,从类生成出对象之后才算得上是真正的有意义!才能开始工作。此时,电视机拥有电视原理图所描述的所有的属性及 方法!明白了吧,呵呵!
我先前介绍过,类是属性与方法的集合。而这些属性与方法可以被声明为私有的(private),公共的(public)或是受保护(protected)的,他们描述了对类成员的访问控制。下面我分别做一下介绍:
1
公共的(public):把变量声明为公共类型的之后,那么就可以通过对象来直接访问,一切都是暴露无遗的!也就是说,你的信用卡密码别人也能够直接得到。
2
私有的(private):如果把变量声明为私有的情况就好多了,想要得到我的信用卡密码,对象必须要调用专用的方法才能够得到。
3
受保护的(protected):介绍继承时再讨论。
为了实现数据的封装,提高数据的安全性,我们一般会把类的属性声明为私有的,而把类的方法声明为公共的。这样,对象能够直接调用类中定义的所有方法,当对 象想要修改或得到自己的属性的时候就必须要调用以定义好的专用的方法才能够实现。你想想,你会把你的信用卡密码公布出来嘛?呵呵!所以,我们提倡的是: “对象调方法,方法改属性”

 构造函数与析构函数

构造函数的作用是创建对象的时候对对象做初始化工作,也就是给用户提供类的成员变量的初始化的一种方式。这同时也包含了内存分配,我觉得这点最重要。

如果程序设计人员自己不定义构造函数的话,编译器会在下列三种情况下提供默认构造函数。

  1. 如果类有虚拟成员函数或者虚基类;
  2. 如果基类有构造函数或者编译器提供的默认构造函数;
  3. 在类中所有的非静态的数据成员,它们所属的类有构造函数或者编译器提供的构造函数。

1和2好理解些,3?类的数据成员所属别的类,那不就是存在继承关系,和2是一个意思?搞不懂。

当一个对象的生命周期完成时,调用析构函数释放对象所占有的资源。析构函数不容许带参数,一个类中也只有一个析构函数,但是可以在析构函数中添加代码,比如动态分配的指针对象所指向的数组。

函数的重载

在同一个类中,函数名相同,参数个数或类型不同,才可以构成函数的重载,只有返回值的类型不同不能构成函数重载。

int output(int a,int b=5);

int output(int a);

上面两个函数符合函数重载的条件,但是当调用output(8)时,由于第一个有默认参数,会出现歧义,所以函数重载的时候要注意默认参数这种情况。

this指针

上代码:

#include <iostream.h>

class point

{

public:

       int x;

       int y;

       point()

       {

              x=0;

              y=0;

       }

       point(int a,int b)

       {

              x=a;

              y=b;

       }

       void input(int x,int y) // input(int q,int w)

       {

              x=x; //x=q

              y=y; //y=w

       }

       void output()

       {

              cout<<"x="<<x<<"y="<<y<<endl;

       }

};

void main()

{

       point pt(5,5); //调用构造函数,将x,y初始化为5

       pt.input(10,10); //调用input函数,将x,y赋值为10???

       pt.output();

}

程序运行之后,变量x,y的初始化工作是正常的,但是赋值失败,书中给出的解释是由于变量的可见性,x,y在input(int x,int y)这个函数中是不可见的。但是把input(int x,int y)形参换成非x,y就可以了,修改代码,测试下果然可以。

这里便有一疑问,既然x,y在input(int x,int y)这个函数中是不可见的,那么即使换了一个形参,也应该是不可见的,个人感觉点地方应该这么来解释:

void input(int x,int y)     {

              x=x;

              y=y;

       }

没有将形参x,y的值赋给类的成员变量x,y并不是因为x,y的可见性,同一个类中的变量不存在不可见性的问题,而是input函数中的x,y相当与两个局部变量,巧合的是和成员变量同名,作用域的优先级高于成员变量(在css样式表中会这样),于是此函数的功能为形参的自我赋值。

回到this指针,每个对象在创建时都会有一个隐含的this指针指向对象本身。将函数修改为:

void input(int x,int y)     {

              this.x=x;

              this.y=y;

       }

由于this指针指向对象本身,当然可以通过this指针指向对象的独有的数据成员,并完成赋值工作。

Ps:又是指针,shit!

继承

派生类成员对基类成员的访问能力

基类成员\继承方式 共有继承 私有继承 保护继承
私有成员 private Private 不可访问 不可访问
保护成员 protected protected private protected
公有成员 public public private protected

在派生类构建构造函数和析构函数时候需要注意:

  •   基类的构造函数和析构函数不能被派生类继承;
  • 如果基类没有定义构造函数,派生类也不能定义构造函数,都要采用默认构造函数(由于编译器对提供构造函数有条件,那么在这种情况下,啥都别干就行了)。
  • 如果基类定义了带参数的构造函数,那么派生类要定义性的构造函数,提供一个将参数传递给基类构造函数的途径。
  • 如果派生类的基类也是派生类,每个派生类只负责直接基类的构。
  •  派生类是否要定义析构函数和所属的基类无关。

基类和派生类的构造函数和析构函数的执行顺序如下:

  • 基类的构造函数
  • 派生类的构造函数
  • 派生类的析构函数
  • 基类的析构函数

虚函数与多态性、纯虚函数

用virtual关键字申明(定义)的函数叫做虚函数,在基类的函数前面加上virtual关键字,在派生类中重写该函数,运行时会根据对象的实际类型来调用相应的函数,这就是c++的多态性,在基类中定义一个统一的接口函数,在不同的派生类中重写该函数,实现不同的功能。

纯虚函数是指被标明为不具体实现的虚成员函数,凡是有纯虚函数的类叫做抽象类。抽象类不能声明对象,只能作为基类为派生类服务,派生类必须完全实现基类的纯虚函数,否则派生类也会变成抽象类。

Virtual void functionName()=0;//纯虚函数

函数的覆盖与隐藏

构成覆盖的条件:

  •  基类函数必须是虚函数;
  • 发生覆盖的2个函数必须分别处于基类和派生类中;
  • 函数名称与参数列表必须完全相同(是不是返回值类型也必须相同?)

构成隐藏的条件:

  • 基类和派生类的函数完全相同,基类没有使用virtual关键字;
  • 基类和派生类的函数名相同,参数不同,此时不管基类函数是否使用virtual关键字,基类的函数都被隐藏。

函数重载发生在同一个类中,覆盖和隐藏发生在基类和派生类关系中。

引用

偶然的机会看到了《Effective C++》中关于引用的建议,对于不需要修改原值的引用,使用引用时尽量加上const关键字,避免对原值造成修改(记不大清楚了,大概是这样)。

引用的定义:int &a=b;

看到一个小程序,不引入第三个变量,交换2个变量的值。

void change(int& a,int& b)

{

       a=a+b;

       b-a-b;

       a=a-b;

}//有一面试的时候被问到了,当时没答出来

VC++程序编译链接的原理和过程

有点小复杂,一时间理解不了,有空慢慢研究。

Comments (1)

  1. 多态性体现在重载和虚函数上把。
    成员对象是有属于自己类的构造函数,和继承不是一个概念。

Post a comment