您的位置:首页 > 编程语言

面对对象编程总结(一)

2017-02-25 13:44 190 查看
一、面对对象编程介绍

面对过程程序 = 数据 + 算法

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

问题 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;
}


空类默认生成的成员

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: