C++ 面向对象 知识点 小结

来源:互联网 发布:js函数式编程书籍 编辑:程序博客网 时间:2024/06/05 18:37

【摘要】

面向对象主要的知识点有,面向对象的基本概念,结构和类的区分,多态及各种构造函数。要求掌握 1.面向对象的基本原则(封装、继承、多态),面向对象的基本概念(类、对象、继承)。2.类和结构体在访问控制上的差异。3.成员变量在静态成员变量的4(3)个特性与静态成员函数的三个特性、引用成员变量必须采用的初始化方式、静态常量成员变量的初始化特点以及成员变量在初始化列表初始化顺序上的特点。 4. string与stack的四个初始化函数的书写。 5. 友元函数的尺度以及实现。 6.异常的2个例题要求熟练掌握。 7. 深拷贝与浅拷贝的概念,知道为什么会有拷贝,为什么会有深拷贝,怎样实现深拷贝。 8. 多态与重载、隐藏、模板的比较,多态中最重要的一句话是什么,多态是面向对象中十分关键的知识点,要非常熟悉!!!9.纯虚函数的产生原因,以及定义方式,熟练掌握。

【正文】

面向对象的一些概念

  • 面向对象的基本概念:类、对象、继承;
  • 面向对象设计的原则:封装、继承、多态;
    • 封装性:封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。(开闭原则)。封装实现了细节隐藏,代码模块化,代码重用。在C++中类中成员的属性有:public, protected, private,这三个属性的访问权限依次降低;
    • 继承性:继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。(里氏代换)继承实现了模块扩展,代码重用;
    • 多态性:多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态实现了模块扩展,接口重用。多态的本质将子类的指针赋给父类的指针,赋值发生就实现了多态,即向上映射。
    • 覆盖,是指子类重新定义父类的虚函数的做法。
    • 重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
  • 面向对象空类默认产生的成员函数:析构、构造、赋值、拷贝函数;


类和结构

  • 类和结构都可以有构造函数、析构函数和继承。但是,类之中的成员默认的访问控制是 private ,结构之中是 public 。

【例】

Test  a (1);// 初始化类对象Test b ( );// 错误的初始化a.fun ( );b.fun ( );


成员变量

  • 静态成员变量可以在类的所有实例之间实现数据共享,可通过 protect、private 实现访问控制约束,不能用初始化列表对静态变量初始化,可以在类体外或者类函数之中对静态变量进行初始化;

静态成员变量

详见 static 静态 成员 变量 和 静态 成员 函数 

详址 http://blog.csdn.net/u013630349/article/details/46414007

  • 私有静态成员变量可以通过公有静态成员函数访问。

【注】

下例在VS2010中是执行成功的

class Myclass

{

private:

static const int period = 30; // interest posted every 30 days

}

VS2010 中,只有静态常量整型数据成员才可以在类中初始化,成员变量、静态成员变量在类中是不能够初始化成功的。

VC6.0中,静态常量整型数据成员、静态成员变量、成员变量都不可在类中初始化;见下例小结

  • 成员变量(注意:这里不是静态成员变量),在初始化列表中先声明的成员变量将会先赋值,及初始化列表的初始化变量顺序是根据成员变量的声明顺序来执行。

【例】

成员函数初始化列表示例

class A 

{

...

int func: val_a (a),val_b(b),val_c(c) { ... }

...

} ;


  • 成员变量的初始化

class test

{

private:
int a; 普通成员
constint b; 常量成员
staticint c; 静态成员
staticconstint d; 静态常量成员
int &e; 引用类型成员
}
记住下面几个原则:1)常量成员(注意没有静态常量成员)和引用类型成员只能用成员初始化列表对成员变量初始化2)静态成员和静态常量成员由于是类共有的,不是属于某一个对象的,因此不能在构造函数中初始化3)静态成员(注意没有静态常量成员)必须在类外初始化4)引用变量必须初始化才能使用5)只有静态常量成员才能在类中直接赋值初始化6)初始化列表初始化变量顺序根据成员变量的声明顺序来执行,而和在程序中赋值先后顺序无关

class test
{
    int a=1;错误,对象还没有构造,尚未分配内存空间
    int a;
    const b;
    static int c = 1;错误,不可以再声明时初始化
    static int c;
    const static int d = 1;唯有静态常量成员才能在类中直接赋值初始化
    int &e;引用类型必须用成员初始化列表
    public:
        test(int _e):b(1),e(_e);引用初始化必须为左值
        test(int _e):b(1),e(_e){};
};
int test::c=1;
const int test::d=1;

构造函数与析构函数

构造函数可以有一个参数也可以有多个参数,构造函数可以重载,即定义多个参数个数不同的函数;但是析构函数不指定数据类型,也没有参数,且一个类中只能定义一个析构函数,不能重载。

析构函数会被自动调用的两种情况:①当这个函数结束时,对象的析构函数被自动调用;②当一个对象使用new运算符被动创建时候,在使用delete运算符释放它,delete将会自动调用析构函数。

  • new对象的时候,其实做了三件事,一是:调用::operator new分配所需内存。二是:调用构造函数。三是:返回指向新分配并构造的对象的指针。
  • delete对象的时候,做了两件事,一是:调用析构函数,二是:调用::operator delete释放内存。
  • 析构函数为什么要采用虚函数

【自己胡诌】析构函数设计为虚函数是因为构造函数为派生类分配了内存空间,调用子类析构函数的时候,将父类的析构函数一并调用;

【官方解释】析构函数设计为虚拟类型是为了所有派生类的函数都可以自动的变为虚拟类,从而避免因析构函数未被调用而导致的内存泄露问题。

  • 构造函数为什么不采用虚函数

【自己胡诌】虚函数是一种需要知道部分成分才能工作的函数机制,也就是对象需要被创建出来,但是构造函数是在对象还没有被构造出来的时候形成的。所以,不能采用虚函数机制。

【官方解释】因为虚调用是一种在要有部分信息的情况下才能工作的机制,允许我们调用一个只知道接口而不知道其准确对象类型的函数。但是,要创建一个对象,必须知道所建对象的类型,因此不能使用虚函数;

  • 是否可以把每一个函数都声明为虚函数

【官方解释】因为每一个虚函数的对象都必须维护一个虚函数表,产生一定系统开销。如果创建的类不需要派生其他类别,根本没必要使用虚函数。

  • 对于一个类X, 如果一个构造函数的第一个参数必须是下列之一

a) X&    b) const X&   c) volatile X&    d) const volatile X&。

  • 析构函数和构造函数都可以是内联函数

【内联函数示例】

inline void fun ()

{

...

}

拷贝函数与赋值函数

  • 熟练掌握 string 的四个初始函数,以及模板类实现栈 。string类、vector类等成熟的类都有各自成熟的拷贝函数。倘若自定义类或结构中存在该类型的数据,可采用浅拷贝,无需自定义拷贝函数;
  • 深拷贝与浅拷贝
详见 :深拷贝 与 浅拷贝 
详址:http://blog.csdn.net/u013630349/article/details/46317987
  • 需要调用拷贝函数的三种情况

1.函数的返回值是对象

2.函数的输入参数是对象

3.采用已有对象初始化新的对象

  • 赋值运算符处理的是两个已有对象,即赋值前下例 A 应该是存在的;复制构造函数是生成一个全新的对象,即调用复制构造函数之前 A 不存在;拷贝构造函数是构造函数,不返回值,而赋值函数需要返回一个对象自身的引用,以便赋值之后的操作。

MyClass &A = (MyClass &B);

MyClass &A(MyClass &B);

  • 如何防止默认拷贝发生?
    声明一个私有拷贝构造函数。甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。
  • 拷贝构造函数一定要使用引用传递,我上网查找了许多资料,大家的意思基本上都是说如果用值传递的话可能会产生死循环。

理解默认拷贝构造函数

【程序】

#include<iostream>using namespace std;class B{private:    int data;public:    B()    {        cout<<"defualt constructor"<<endl;    }    ~B()    {        cout<<"destructed "<<endl;    }    B( int i) : data(i)    {        cout<<"constructed by parameter"<<data<<endl;    }};B Play( B b ){    return b;}int main (){    B temp = Play(5);    return 0;}

1)该程序输出结果是什么?为什么会有这样的输出?

2)B(int i):data(i),这种用法的专业术语叫什么?

3)Play(5),形参类型是类,而5是个常量,这样写合法吗?为什么?

(1)输出结果如下:

constructed by parameter   在Play(5)处,5通过隐含的类型转换调用了B::B( int i )

destructed                              Play(5) 返回时,参数的析构函数被调用

destructed                              temp的析构函数被调用;temp的构造函数调用的是编译器生存的拷贝构造函数,这个需要特别注意

【注】

这里确实还有一个复制构造函数没有体现出来,就是在函数返回的时候,不过,这个构造函数的形式与上述构造函数是不一样的。

    B( int i) : data(i)    {        cout<<"constructed by parameter"<<data<<endl;    }
    B(B &b)    {        cout<<"constructed by obj !!!"<<endl;    }
【注】

函数返回,栈回缩机制,导致所申请的空间被撤销所创建的对象也会消失!!!

(2)带参数的构造函数,冒号后面的是成员变量初始化列表(member initialization list)
(3)合法。单个参数的构造函数如果不添加explicit关键字,会定义一个隐含的类型转换;添加explicit关键字会消除这种隐含转换。

多态、重载、模板

  • 重载是输入参数不同的同名函数。编译器根据函数的不同参数对同名函数做修饰,在编译时就已经确定调用的是哪个函数了。即,静态调用,在编译时就已经绑定了函数的地址。
  • 多态覆盖实现的,是子类对基类方法的再声明。动态调用子类函数,在编译时无法确定调用的是哪一个函数,只有在运行时才能确定,运行时通过多态实现。多态是面向对象最重要的特性,多态性可以理解为一个接口多种方法。
  • 多态指针一致:子类虚函数返回的多态指针的静态类型是父类函数所返回的多态指针动态类型集合中的某个类型;【换言之,与众多基类虚函数对应的子类函数,与其对应函数的返回值是一致的。】
  • 覆盖与重载在输入输出参数上的比较分析:
    • 成员函数被重载的特征:(1)相同的范围(在同一个类中);(2)函数名字相同;(3)参数不同;(4)virtual关键字可有可无;(5)异常数目、异常类型、访问控制不同都不构成重载参数不同的要求。
    • 覆盖是指派生类函数覆盖基类函数,特征是:(1)不同的范围(分别位于派生类与基类);(2)函数名字相同;(3)参数相同;(4)基类函数必须有virtual关键字;(5)覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;(6)被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
    • 覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。
  • 隐藏是指派生类的函数屏蔽了与其同名的基类函数。覆盖、重载与隐藏的关系 
  • 详见:C++ 覆盖 重载 隐藏 浅析
  • 详址:http://blog.csdn.net/u013630349/article/details/46706299


  • 多态中最重要的一句话是什么,大声说出来!!!

指针的类型是实函数的类型,指向对象的类型是虚函数的类型。


纯虚函数

1)为什么会有纯虚函数?

为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数方法:

virtual ReturnType Function()= 0;

编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类它不能生成对象。这样就很好地解决了上述两个问题。
2)纯虚函数是什么,怎样定义?
纯虚函数是定义在基类中的虚函数,它在基类中无定义,要求任何派生类自己去实现该方法,在基类中纯虚函数的形式如下:

virtual void func( ) = 0;

【初始化 赋值 定义 声明 的 区别】

  • 初始化创建变量并给它赋初值。初始化和赋初值不同。初始化 = 带指定初始值的定义。一个程序中,一个变量只能初始化一次。如:int  i=0;
  • 赋值:擦除对象的当前值并用新值替代,在一个程序中,一个变量可以多次赋值。
  • 定义:用来为变量分配内存空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。例:int  i;(单纯定义)  int  i = 0;(带指定初始值的定义)
  • 声明:用来向程序表明变量的类型和名字。定义也是声明,当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量而不是定义它。例:extern  int  i;


【答疑】

MSBD P 123.5 ~ 127.1 ~ 128.2 ~129.10.8

多态指针

tchar.h

_tmain

接口,接口重用?

constvolatile

【复习要点】

http://blog.csdn.net/to_xidianhph_youth/article/details/38906367

0 0
原创粉丝点击