面对对象编程总结(一)

一、面对对象编程介绍

面对过程程序 = 数据 + 算法
面对对象程序 = 对象 + 消息 (提高代码的维护性、复用性、扩展性、灵活性)

问题 1、面对对象的特点

* 抽象(代码的灵活性)
* 封装(代码的维护性)
* 继承(代码的复用性)
* 多态(代码的扩展性)

1、在C++中,主要用类来解决封装性
这里写图片描述

2、继承

这里写图片描述
3、多态

这里写图片描述

面对对象编程的优点

这里写图片描述

二、类与对象(类是对象的抽象,对象是类的实例化)

1、类的声明
class 类名
{
public:
共有成员(外部接口)
private:
私有成员;
protected:
保护成员

};
在关键字public后面声明,它们是类与外部的接口,外部函数都可以访问共有类型数据和函数。
在private后面声明,只允许本类中的函数来访问,而外部的任何函数都不能来访问。
在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同。

2、成员函数
类外实现与类内实现成员函数,但类内实现的函数都是inline函数,占用内存。

什么是inline函数:编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去。
以空间换执行时间。

3、类与结构的区别

在C++中,结构体几乎和类一样,也能放函数。
区别:
(1)class成员默认为private,struct 默认为public
(2)类的实例对象的大小由属性决定,也遵循内存的自对齐方式。
方法的空间是共享的。

4、对象的存储模型,什么是this指针,功能是什么?
this指针的解释:
1)一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。
2)this指针是类的一个自动生成、自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象。全局仅有一个this指针,当一个对象被创建时,this指针就存放指向对象数据的首地址。
this指针的通俗解释:
当你进入一个房子后,你可以看见桌子、椅子、地板等,但是房子你是看不到全貌了。对于一个类的实例来说,你可以看到它的成员函数、成员变量,但是实例本身呢?this是一个指针,它时时刻刻指向你这个实例本身。
为什么需要this指针?
因为this作用域是在类的内部,自己声明一个类的时候,还不知道实例化对象的名字,所以用this来使用对象变量的自身。在非静态成员函数中,编译器在编译的时候加上this作为隐含形参,通过this来访问各个成员(即使你没有写上this指针)。例如a.fun(1)<==等价于==>fun(&a,1)

this的使用:1)在类的非静态成员函数中返回对象的本身时候,直接用return *this(常用于操作符重载和赋值、拷贝等函数)。2)传入函数的形参与成员变量名相同时,例如:this->n = n (不能写成n=n)

//程序举例(转)
class Point

{
    int x, y;

public:
    Point(int a, int b) { x=a; y=b;}
    Void MovePoint( int a, int b){ x+=a; y+=b;}
    Void print(){ cout<<"x="< };

void main( )
{
     Point point1( 10,10);
     point1.MovePoint(2,2);
     point1.print( );

}

a.对象point1调用MovePoint(2,2)的时候,即将point1对象的地址传递给了this指针
b.编译器编译后的原型应该是void MovePoint(Point *this, int a, int b)
c.在函数体中可以写成{this->x += a; this->y += b;}
d.也等价为point1.x += a;point1.y += b。(指针变量通过->访问成员变量(函数),对象通过.)

5、类的作用域
前向声明的类不能实例化,在类中只能定义类的对象指针和引用。

6、嵌套类和局部类
嵌套类是在类里面再定义一个类
局部内在函数体内定义,局部类不能有静态成员和方法,必须在类内实现方法。

三、构造函数与析构函数

1、构造函数
什么是构造函数?构造函数是特殊的成员函数,创建类型的新的对象,系统会自动调用构造函数,构造函数是为了保证对象的每个数据成员都能被正确初始化。

构造函数的特点:
(1)函数名和类名完全相同。
(2)构造函数不能定义返回类型,不能有返回值,也不能使用void。
(3)通常情况下构造函数应声明为公有函数,否则不能像其他成员函数被显式的调用。
(4)构造函数被声明为私有有特殊的用途
(5)构造函数可以重载

什么是默认构造函数(不带参数的构造函数)

全局对象的构造函数现于main函数执行

构造函数与new运算符?

/*使用NEW运算符在堆中开辟一块新空间,例程如下:
#include <iostream>
using std::cout;
class A
{
public:
A(){cout<<"构造函数执行完毕\n";};

};
int main()
{
A *p;     //声明一个属于A类的指针p
p=new A; //使用new运算符创建一块堆中空间,它的大小由类A的数据成员的类型和数量来定,由于堆在内存中都是匿名的,因此这里没有为它命名,只是将它的内存地址赋给了指针p
return 0;
}
*/
//使用new运算符创建并实例化一个对象,例程如下:
#include <iostream>
using std::cout;
class A
{
public:
A(int a,int b){i=a;j=b;cout<<"构造函数执行完毕\n";};
void print(){cout<<i*j<<"\n";}
void set(int a,int b){i=a;j=b;}
private:
int i,j;
};
int main()
{
A *p=new A(1,2); //在创建一个对象的同时实例化它的数据成员
p->print();   //输出结果
p->set(7,8);   //利用成员函数访问该对象的数据成员并赋值
p->print();   //输出结果
return 0;
}  

使用new运算符建立一个类的对象时,比如说类A的对象,new首先分配足以保证该类的一个对象所需要的内存,然后自动调用构造函数来初始化这块内存,再返回这个对象的地址。
2、析构函数
什么时候析构函数?
析构函数的特点
(1)函数名与类名相识(前面带~)
(2)没有返回类型
(3)析构函数不能重载
(4)系统会默认生成一个析构函数

析构函数与数组的得出构造函数与析构函数的调用顺序相反。

以new创建的对象,通delete才能触发析构函数。

构造函数详解

* 转换构造函数

什么是转转换构造函数?
单个参数的构造函数。是将其他类型转换为类类型。但只有一个参数是非常危险的,因为编译器可以使用这种函数把参数的类型隐式转换为类类型。

//不带参数的构造函数为默认构造函数
Test::Test()
{
    num_ = 0;
}

//转换构造函数
Test::Test(int num)
{
    num_ = num;

}

int main()
{
    Test t(10);//带一个参数的构造函数,充当的是普通构造函数的功能
    t = 20;//将20这个整数赋值给t这个对象。
    //1.调用转换构造函数将20这个整数转换为类类型。(生成一个临时对象)
    //2.将临时对象赋值给t对象(调用的是=运算符)
    return 0;
}
* 赋值与初始化的区别

Test t = 5;//初始化
Test t;
t = 5;//赋值
在初始化语句中等号不是运算符。编译器对这种表述方法有特殊的解释。
赋值:Test & Test::operator=(const & Test & other);

  • explicit

explicit关键字的作用:编译器不会把声明为explicit的构造函数用于隐式转换,它只能在程序代码中显示创建对象。

  • 构造函数初始化列表
class Object
{
public:
    Object(int num = 0):num_(num),knum_(num)
    {

private:
    int num_;
    int knum_;
};

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:

class CExample
{

public:
 int a;
float b;
//构造函数初始化列表
 CExample(): a(0),b(8.8)
 {
 }
  //构造函数内部赋值
  CExample()
  {
    a=0;
    b=8.8;
  }

};

上面的例子中两个构造函数的结果是一样的。上面的构造函数(使用初始化列表的构造函数)显式的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化。

初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。对非内置类型成员变量,为了避免两次构造,推荐使用类构造函数初始化列表。但有的时候必须用带有初始化列表的构造函数:

1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。

2.const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

初始化数据成员与对数据成员赋值的含义是什么?有什么区别?

首先把数据成员按类型分类并分情况说明:

1.内置数据类型,复合类型(指针,引用)

在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的

2.用户定义类型(类类型)

结果上相同,但是性能上存在很大的差别。因为类类型的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,调用构造函数,在进入函数体之后,进行的是对已经构造好的类对象的赋值,又调用个拷贝赋值操作符才能完成(如果并未提供,则使用编译器提供的默认按成员赋值行为)

Note:
初始化列表的成员初始化顺序:
C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。

 Example:
 class CMyClass
 {

    CMyClass(int x, int y);
    int m_x;
    int m_y;
};

CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y)
{
}

你可能以为上面的代码将会首先做m_y=I,然后做m_x=m_y,最后它们有相同的值。但是编译器先初始化m_x,然后是m_y,,因为它们是按这样的顺序声明的。结果是m_x将有一个不可预测的值。有两种方法避免它,一个是总是按照你希望它们被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。

必须使用初始化列表的初始化:

(1)成员对象
(2)const成员
(3)引用成员。

原因如下:

1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。

2.const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

  • 对象成员及其初始化

对象成员(对象所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行

  • 枚举适用于所有对象

拷贝构造函数总结
作用:作用:使用一个已经存在的对象来初始化一个新的同一类的对象
声明:只有一个参数并且参数为该类对象的引用。

Test::Test(const Test & other):num(other.num_)
{
    num_ = other.num_;
}

拷贝函数三种调用情况:
1、用已有的对象去初始化对象
2、当函数的形参是类的对象,调用函数
3、当函数的返回值是类对象,函数执行完成返回调用者时使用

  • 深拷贝与浅拷贝

    在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
      深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

禁止拷贝:
对于读一无二的对象禁止拷贝,将拷贝函数私有化。

代码

#ifndef _TEST_H_
#define _TEST_H_
class CA
{
public:
       CA();
       CA(int b,char *cstr);
       ~CA();
       CA(const CA & other);
       void Display();
private:
       int a_;
       char *str_;
};
#endif
#include "Test.h"
#include <iostream>
#include <string.h>
using namespace std;
CA::CA()
{
}
CA::CA(int b,char *cstr)
{
       a_ = b;
       str_ = new char [b];
       strcpy(str_,cstr);
}
CA::CA(const CA & other)
{
       a_= other.a_;
       str_ = new char [a_];
       if(str_ != NULL)
       {
              strcpy(str_,other.str_);
       }
}
CA::~CA()
{
       delete str_;
}
void CA::Display()
{
       cout << str_ << endl;
}

#include <iostream>
#include "Test.h"
using namespace std;
int main()
{
       CA A(10,"hello");
       CA B = A;
       B.Display();
       return 0;
}
  • 空类默认生成的成员

    这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值