C++面向对象

C++面向对象模型初探

C++对象模型可以概括为以下2部分:

  • 语言中直接支持面向对象程序设计的部分,主要涉及如构造函数、析构函数、虚函数、继承(单继承、多继承、虚继承)、多态等等。
  • 对于各种支持的底层实现机制。

  • 在c语言中,“数据”和“处理数据的操作(函数)”是分开来声明的,也就是说,语言本身并没有支持“数据和函数”之间的关联性。
  • 在c++中,通过抽象数据类型(abstract data type,ADT),在类中定义数据和函数,来实现数据和函数直接的绑定。

在C++类中有两种成员数据:static、nonstatic;三种成员函数:static、nonstatic、virtual。
C++类

类、对象、成员变量、成员函数

类,是一个抽象数据类型
对象,我们用类去定义对象
成员变量,C++中用于表示类属性的变量
成员函数,C++中用于表示类行为的函数

封装、多态、继承

封装

  • 把属性和方法进行封装,对属性和方法进行访问控制
  • 对类的访问控制:
    • Public修饰成员变量和成员函数可以在类的内部和类的外部被访问
    • Private修饰成员变量和成员函数只能在类的内部被访问

struct 与 class的区别

  • 在用struct定义类时,所有成员的默认属性为public
  • 在用class定义类时,所有成员的默认属性为private

构造函数与析构函数

构造函数:

  • constructor来处理对象的初始化
  • 构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。

constructor的调用

  • 自动调用:一般情况下C++编译器会自动调用构造函数
  • 手动调用:在一些情况下则需要手工调用构造函数
  • 没有任何返回类型的声明

析构函数:

  • C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
  • 析构函数需要照顾对象的属性的内存生命周期
  • 析构函数没有参数也没有任何返回类型的声明
  • 析构函数在对象销毁时自动被调用
  • C++编译器自动调用
copy构造函数
    Test4() //无参数构造函数
    {
        m_a = 0;
        m_b = 0;
        cout<<"无参数构造函数"<<endl;
    }
    Test4(int a, int b) //有参数构造函数 //3种方法
    {
        m_a = a;
        m_b = b;
        cout<<"有参数构造函数"<<endl;
    }
    //赋值构造函数 (copy构造函数) //
    Test4(const Test4& obj )
    {
        cout<<"我也是构造函数 " <<endl;
        m_b = obj.m_b + 100;
        m_a = obj.m_a + 100;
    }
默认构造函数

2个特殊的构造函数

  • 默认无参构造函数
    当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
  • 默认拷贝构造函数
    当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制

构造函数调用规则

  • 当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
  • 当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
  • 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
  • 默认拷贝构造函数成员变量简单赋值

总结:只要你写了构造函数,那么你必须用。

构造析构阶段性总结

  1. 构造函数是C++中用于初始化对象状态的特殊函数
  2. 构造函数在对象创建时自动被调用
  3. 构造函数和普通成员函数都遵循重载规则
  4. 拷贝构造函数是对象正确初始化的重要保证
  5. 必要的时候,必须手工编写拷贝构造函数

深copy与浅copy

浅拷贝

  • 指针变量被赋值,但是指针变量所指向的内存空间未被赋值。

深拷贝

  • 把对象的所有属性值和内存空间都拷贝

浅拷贝原因

  • 因为obj2只是copy对象obj1的属性值,和指针值,所以为指针在堆区里面再分配内存空间,即obj1与obj2的指针指向的内存空间是同一个,故在调用析构函数时,先析构掉obj2指针所指向的内存空间,这时obj1的指针就变为了野指针。而再调用obj1的析构函数时,会发现指针指向的内存空间已经被析构掉了,这样就会发生core dump。
    浅拷贝原因分析
    等号操作
    对象的等号赋值
    解决浅拷贝的办法就是深拷贝,C++默认的拷贝构造函数与等号操作都是浅拷贝

对象初始化列表

  • 如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
    错误原因与浅拷贝原因一致
    解决办法是重载等号操作符或者调用对象初始化列表
  • 当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。

语法规则
Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
{
// some other assignment operation
}

  • 注意概念
    初始化:被初始化的对象正在创建
    赋值:被赋值的对象已经存在

注意

  • 成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
  • 初始化列表先于构造函数的函数体执行

class ABC{
public:
         ABC(int a, int b, int c)
         {
                  this->a = a;
                  this->b = b;
                  this->c = c;
                  printf("a:%d,b:%d,c:%d \n", a, b, c);
                  printf("ABC construct ..\n");
         }
         ~ABC()
         {
                  printf("a:%d,b:%d,c:%d \n", a, b, c);
                  printf("~ABC() ..\n");
         }
protected:
private:
         int a;
         int b;
         int c;
};
class MyD
{
public:
        //初始化成员列表
         MyD():abc1(1,2,3),abc2(4,5,6),m(100)
         //MyD()
         {
                  cout<<"MyD()"<<endl;
         }
         ~MyD()
         {
                  cout<<"~MyD()"<<endl;
         }
protected:
private:
         ABC abc1; //c++编译器不知道如何构造abc1
         ABC abc2;
         const int m;
};
int main()

{
         MyD myD;
         return 0;
}

对象的动态建立与释放

new 与 delete

  • 在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。
  • 在C语言中是利用库函数malloc和free来分配和撤销内存空间的。
  • C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数

注意new和delete是运算符,不是函数,因此执行效率高
new运算符的例子

new int;  //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针)
new int(100);  //开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址new char[10];  //开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址
new int[5][4];  //开辟一个存放二维整型数组(大小为5-4)的空间,返回首元素的地址;
float *p=new float (3.14159);  //开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p

new 与 delete 使用格式
new 与 delete

类对象的动态建立与释放

使用类名定义的对象都是静态的,在程序运行过程中,对象所占的空间是不能随时释放的。但有时人们希望在需要用到对象时才建立对象,在不需要用该对象时就撤销它,释放它所占的内存空间以供别的数据使用


C++中,可以用new运算符动态建立对象,用delete运算符撤销对象
比如:

Box *pt;  //定义一个指向Box类对象的指针变量pt    
pt=new Box;  //在pt中存放了新建对象的起始地址在程序中就可以通过pt访问这个新建的对象。
cout<<pt->height;  //输出该对象的height成员    
cout<<pt->volume( );  //调用该对象的volume函数,计算并输出体积

C++还允许在执行new时,对新建立的对象进行初始化.

Box *pt=new Box(12,15,18);

这种写法是把上面两个语句(定义指针变量和用new建立新对象)合并为一个语句,并指定初值,这样更精炼。
新对象中的height,width和length分别获得初值12,15,18。调用对象既可以通过对象名,也可以通过指针。
在执行new运算时,如果内存量不足,无法开辟所需的内存空间,目前大多数C++编译系统都使new返回一个0指针值。只要检测返回值是否为0,就可判断分配内存是否成功。

ANSI C++标准提出,在执行new出现故障时,就“抛出”一个“异常”,用户可根据异常进行有关处理。但C++标准仍然允许在出现new故障时返回0指针值。当前,不同的编译系统对new故障的处理方法是不同的。
在不再需要使用由new建立的对象时,可以用delete运算符予以释放.

delete pt; //释放pt指向的内存空间
这就撤销了pt指向的对象。此后程序不能再使用该对象。
如果用一个指针变量pt先后指向不同的动态对象,应注意指针变量的当前指向,以免删错了对象。在执行delete运算符时,在释放内存空间之前,自动调用析构函数,完成有关善后清理工作。

静态成员变量成员函数

静态成员变量

  • 关键字 static 可以用于说明一个类的成员,静态成员提供了一个同类对象的共享机制
  • 把一个类的成员说明为 static 时,这个类无论有多少个对象被创建,这些对象共享这个 static 成员
  • 静态成员局部于类,它不是对象成员
    静态成员变量
#include<iostream>
using namespace std;
class  counter
{     
static  int  num ; //声明与定义静态数据成员
  public :
      void  setnum ( int i ) { num = i ; }        //成员函数访问静态数据成员
      void  shownum() { cout << num << '\t' ; }
} ;
int  counter :: num = 0 ;//声明与定义静态数据成员
void main ()
{   counter  a , b ;
    a.shownum() ; //调用成员函数访问私有静态数据成员
    b.shownum() ;
    a.setnum(10) ;
    a.shownum() ;
    b.shownum() ;
}

静态成员函数

  • 静态成员函数数冠以关键字static
  • 静态成员函数提供不依赖于类数据结构的共同操作,它没有this指针
  • 在类外调用静态成员函数用 “类名 :: ”作限定词,或通过对象调用
    静态成员函数

疑难问题:静态成员函数中,不能使用普通变量
静态成员变量属于整个类的,分不清楚,是那个具体对象的属性。
静态成员函数的使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章