面对对象编程总结(一)
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)
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运算符建立一个类的对象时,比如说类A的对象,new首先分配足以保证该类的一个对象所需要的内存,然后自动调用构造函数来初始化这块内存,再返回这个对象的地址。
2、析构函数
什么时候析构函数?
析构函数的特点
(1)函数名与类名相识(前面带~)
(2)没有返回类型
(3)析构函数不能重载
(4)系统会默认生成一个析构函数
析构函数与数组的得出构造函数与析构函数的调用顺序相反。
以new创建的对象,通delete才能触发析构函数。
构造函数详解
什么是转转换构造函数?
单个参数的构造函数。是将其他类型转换为类类型。但只有一个参数是非常危险的,因为编译器可以使用这种函数把参数的类型隐式转换为类类型。
Test t = 5;//初始化
Test t;
t = 5;//赋值
在初始化语句中等号不是运算符。编译器对这种表述方法有特殊的解释。
赋值:Test & Test::operator=(const & Test & other);
explicit
explicit关键字的作用:编译器不会把声明为explicit的构造函数用于隐式转换,它只能在程序代码中显示创建对象。
构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:
上面的例子中两个构造函数的结果是一样的。上面的构造函数(使用初始化列表的构造函数)显式的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化。
初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。对非内置类型成员变量,为了避免两次构造,推荐使用类构造函数初始化列表。但有的时候必须用带有初始化列表的构造函数:
1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
2.const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。
初始化数据成员与对数据成员赋值的含义是什么?有什么区别?
首先把数据成员按类型分类并分情况说明:
1.内置数据类型,复合类型(指针,引用)
在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的
2.用户定义类型(类类型)
结果上相同,但是性能上存在很大的差别。因为类类型的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,调用构造函数,在进入函数体之后,进行的是对已经构造好的类对象的赋值,又调用个拷贝赋值操作符才能完成(如果并未提供,则使用编译器提供的默认按成员赋值行为)
Note:
初始化列表的成员初始化顺序:
C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
你可能以为上面的代码将会首先做m_y=I,然后做m_x=m_y,最后它们有相同的值。但是编译器先初始化m_x,然后是m_y,,因为它们是按这样的顺序声明的。结果是m_x将有一个不可预测的值。有两种方法避免它,一个是总是按照你希望它们被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。
必须使用初始化列表的初始化:
(1)成员对象
(2)const成员
(3)引用成员。
原因如下:
1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
2.const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。
对象成员及其初始化
对象成员(对象所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行
枚举适用于所有对象
拷贝构造函数总结
作用:作用:使用一个已经存在的对象来初始化一个新的同一类的对象
声明:只有一个参数并且参数为该类对象的引用。
拷贝函数三种调用情况:
1、用已有的对象去初始化对象
2、当函数的形参是类的对象,调用函数
3、当函数的返回值是类对象,函数执行完成返回调用者时使用
深拷贝与浅拷贝
在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
禁止拷贝:
对于读一无二的对象禁止拷贝,将拷贝函数私有化。
代码
空类默认生成的成员
面对过程程序 = 数据 + 算法
面对对象程序 = 对象 + 消息 (提高代码的维护性、复用性、扩展性、灵活性)
问题 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; }
空类默认生成的成员
相关文章推荐
- 面对对象编程总结(二)
- 总结两个Javascript的哈稀对象的一些编程技巧
- Java面对对象编程---学习笔记(序目)
- Symbian编程总结-关键篇-活动对象正解(3)-活动对象的工作原理
- 【COCOS2DX-LUA 脚本开发之五】Lua 使用OOP(面对对象思想编程),免Binding创建自定义lua类
- Symbian编程总结-基础篇-活动对象正解(4)-异步函数的同步调用
- Symbian编程总结-基础篇-活动对象正解(2)-使用活动对象
- 事件驱动的JScript面对对象编程(例)
- Symbian编程总结-基础篇-活动对象正解(1)-理解活动对象
- 总结两个Javascript的哈稀对象的一些编程技巧
- Symbian编程总结-基础篇-活动对象正解(3)-活动对象的工作原理
- 总结两个Javascript的哈稀对象的一些编程技巧
- Symbian编程总结-基础篇-集合与缓冲区(2)-验证RArray::Append是否保存对象副本
- Symbian编程总结-基础篇-活动对象正解(1)-理解活动对象
- Symbian编程总结-关键篇-活动对象正解(2)-使用活动对象
- 总结两个Javascript的哈稀对象的一些编程技巧
- 总结两个Javascript的哈稀对象的一些编程技巧
- Symbian编程总结-基础篇-活动对象正解(3)-活动对象的工作原理
- 面对对象编程初步
- 11. 面对对象编程