`
huobengluantiao8
  • 浏览: 1050160 次
文章分类
社区版块
存档分类
最新评论

C++语言特性:构造函数,析构函数,虚函数,内联函数,静态成员函数,重载,覆盖,隐藏

 
阅读更多

C++中class默认属性为private, struct默认属性为public

构造函数

1.初始化对象成员;
2.创建类对象;
由于虚函数是建立在对象的基础上的,因此构造函数不能声明为虚函数;虚函数是在执行的时候才识别,根据具体对象进行动态绑定.
每个类对象都有一个默认构造函数.当一个对象被在堆上创建的时候,第一步先执行new操作,第二步才会执行构造函数体,因此尽可能不要在构造函数内部动态申请太多的资源,以免引起内存泄露.
详情如下:
cppBaseClass *base = new cppBaseClass();
012A1FDD  push        0Ch  
012A1FDF  call        operator new (12A11E5h)  
012A1FE4  add         esp,4  
012A1FE7  mov         dword ptr [ebp-11Ch],eax  
012A1FED  mov         dword ptr [ebp-4],0  
012A1FF4  cmp         dword ptr [ebp-11Ch],0  
012A1FFB  je          main+70h (12A2010h)  
012A1FFD  mov         ecx,dword ptr [ebp-11Ch]  
012A2003  call        cppBaseClass::cppBaseClass (12A1023h)  
012A2008  mov         dword ptr [ebp-130h],eax  
012A200E  jmp         main+7Ah (12A201Ah)  
012A2010  mov         dword ptr [ebp-130h],0  
012A201A  mov         eax,dword ptr [ebp-130h]  
012A2020  mov         dword ptr [ebp-128h],eax  
012A2026  mov         dword ptr [ebp-4],0FFFFFFFFh  
012A202D  mov         ecx,dword ptr [ebp-128h]  
012A2033  mov         dword ptr [ebp-14h],ecx  


拷贝构造函数[复制构造函数]
如果要动态地为本地C++类的成员分配空间,则必须实现复制构造函数.
下面是拷贝构造函数的实现模版:
          ConstructorFunctionClassName(const ConstructorFunctionClassName& ObjectType)
          {
          }
拷贝构造函数的调用条件:
1.当一个对象以值传递的方式传入函数体;
2.当一个对象以值传递的方式作为函数的返回值;
3.当一个对象通过另外一个对象进行初始化;
为什么需要实现拷贝构造函数?

首先这里需要描述一下C++中默认拷贝构造函数,参考下面代码:

          className Object0("Test the constructor function");
          className Object1 = Object0; //默认构造函数被调用

默认构造函数会把类对象Object0的指针成员存储地址复制到Object1中,这个时候Object1和Object0同时指向同一块内存区域, 那么如果Oject1被销毁了,那么Object0中的内容就会发生改变.
上面的现象也被OOP中称为浅拷贝.
那对应的肯定有深拷贝, Object0拥有资源,当上面的Object1在复制的过程中重新分配了资源,那么这个过程就是深拷贝.
        className cName;
        className::className(cName);

在传递对象cName之前,编译器需要安排创建该对象的副本.因此,编译器为了处理复制构造函数的这条语句,需要调用复制构造函数来创建实参的副本。由于是按值传递,第二次调用同样需要创建实参的副本,因此还得调用复制构造函数,产生无穷调用.
看下面的一段代码:
        className  cName("Test default constructor");
        testDefaultConstructor(cName);
         void testDefaultConstructor( className Object0)
         {
                Object0.PrintString();
         } 

实参cName是作为传值的方式传递的,那么Object0将导致默认构造函数被调用:
1.创建cName对象;
2.由于cName是传值方式,因此导致Object0将要创建一个副本,而且该副本同时指向原来对象所指向的数据成员.
3.当退出testDefaultConstructor函数的时候, Object0超出了其作用域,那么Object0的析构函数将要被调用,Object0指向的数据成员将要被释放.
4.当函数从testDefaultConstructor返回的时候, cName对象依然指向之前Object0指向的数据区域.异常发生!

PS:

当类对象中有指针成员时必须用深拷贝,浅拷贝会导致指针成员变成野指针的风险;

传值的时候,对象的副本产生,但是该副本和原始对象都包含了指针成员,同时该指针成员指向的地址是相同的,那么在完成了传值的作用域之后,该副本对象会自动销毁,因此必然导致该副本的析构函数被调用[通常会在析构函数中释放指针成员的资源],那么当真正的对象被习够的时候,就会再次处理该指针成员.

析构函数:

1.给对象提供释放资源的机会;
2.销毁对象,销毁不再需要或超出其作用域的对象.当对象超出其作用域时,程序将自动调用类的析构函数.
如果想要防止对像被创建在堆上,可以私有化析构函数,不过这样以来该类不能被继承.
如果需要调用私有析构函数,则需要实现一个成员函数,在该成员函数内部调用
delete this;

虚拟析构函数:
如果当前类作为接口基类,那么需要声明该类的析构函数为虚拟析构函数.
因为如果,当我们使用基类的指针去删除派生类的对象的时候,如果基类的析构函数不是虚拟析构函数,那么派生类的析构函数将会不被执行.
但是当生命了虚函数之后,在对象中就会生成一个虚函数表,这样会增加类对象的存储空间开销.
下面部分是基于基类虚拟析构函数的条件下反汇编出来的:

cppBaseClass *base = new cppDeriveClass();
002D235D  push        10h  
002D235F  call        operator new (2D121Ch)  
002D2364  add         esp,4  
002D2367  mov         dword ptr [ebp-104h],eax  
002D236D  mov         dword ptr [ebp-4],0  
002D2374  cmp         dword ptr [ebp-104h],0  
002D237B  je          main+70h (2D2390h)  
002D237D  mov         ecx,dword ptr [ebp-104h]  
002D2383  call        cppDeriveClass::cppDeriveClass (2D1069h)  
002D2388  mov         dword ptr [ebp-118h],eax  
002D238E  jmp         main+7Ah (2D239Ah)  
002D2390  mov         dword ptr [ebp-118h],0  
002D239A  mov         eax,dword ptr [ebp-118h]  
002D23A0  mov         dword ptr [ebp-110h],eax  
002D23A6  mov         dword ptr [ebp-4],0FFFFFFFFh  
002D23AD  mov         ecx,dword ptr [ebp-110h]  
002D23B3  mov         dword ptr [ebp-14h],ecx  
delete base;
002D23B6  mov         eax,dword ptr [ebp-14h]  
002D23B9  mov         dword ptr [ebp-0ECh],eax  
002D23BF  mov         ecx,dword ptr [ebp-0ECh]  
002D23C5  mov         dword ptr [ebp-0F8h],ecx  
002D23CB  cmp         dword ptr [ebp-0F8h],0  
002D23D2  je          main+0D9h (2D23F9h)  
002D23D4  mov         esi,esp  
002D23D6  push        1  
002D23D8  mov         edx,dword ptr [ebp-0F8h]  
002D23DE  mov         eax,dword ptr [edx]  
002D23E0  mov         ecx,dword ptr [ebp-0F8h]  
002D23E6  mov         edx,dword ptr [eax]  
002D23E8  call        edx  
002D23EA  cmp         esi,esp  
002D23EC  call        @ILT+445(__RTC_CheckEsp) (2D11C2h)  
002D23F1  mov         dword ptr [ebp-118h],eax  
002D23F7  jmp         main+0E3h (2D2403h)  
002D23F9  mov         dword ptr [ebp-118h],0  
return 0;
002D2403  xor         eax,eax  
}

虚函数:

虚函数主要是实现了面向对象中的多态的作用.通俗的讲就是通过基类的指针指向派生类的对象,用基类的指针来调用派生类的成员函数. 这种技术可以让派生类拥有“多种形态”,这是一种泛型技术, 通过使用不变的代码来实现可变的算法.比如:模版技术, RTTI[Run-Time Type Identification], 虚函数技术.
虚函数是以virtual关键字声明的基类函数.如果在基类中将某个函数指定为virtual,并且派生类中有该函数的另外一个定义,则编译器将知道我们不想静态链接该函数.
当基类中声明了虚函数,那么根据调用该函数的当前对象的类型,选择派生类中出现的该函数的其他定义.
纯虚函数, 通过在函数声明最后添加=0,可以将本地C++基类中的虚函数定义为纯虚函数. 那么该类就称为不能创建任何对象的抽象类,在任何派生类中,都必须定义所有纯虚函数.如果不是,则该派生类也将称为抽象类.
根据虚函数定义后的虚函数表,基类指针既可以指向派生类的成员,也可以指向基类的成员.
虚函数表,
下面部分内容参考陈皓专栏

http://blog.csdn.net/haoel/article/details/1948051

cppVirtualFunctionTable.cpp

#include <iostream>
using std::cout;
using std::endl;

class baseClass
{
public:
	virtual void a(){ cout<<"baseClass function a()"<<endl;}
	virtual void b(){ cout<<"baseClass function b()"<<endl;}
	virtual void c(){ cout<<"baseClass function c()"<<endl;}
};

class deriveClass: public baseClass
{
public:
	void a(){ cout<<"deriveClass function a()"<<endl;}
	void b1(){ cout<<"deriveClass function b1()"<<endl;}
	void c1(){ cout<<"deriveClass function c1()"<<endl;}
};

main.cpp

#include "cppVirtualFunctionTable.cpp"
int main(int argc, char** argv)
{
     // define the Func is an alias for the function pointer.
     typedef void(*Func)(void);

     baseClass cBase;
     Func pFunc = NULL;
     cout<<"虚函数表地址:"<<(int*)(&cBase)<<endl;
     cout<<"虚函数表第一个函数地址:"<<(int*)*(int*)(&cBase)<<endl;

     // Invoke first virtual function
     pFunc = (Func)*((int*)*(int*)(&cBase));
     pFunc();
     return 0;
}


环境:Microsoft Visual Studio 2010, Window 7

输出结果
虚函数表地址:0031fce4
虚函数表第一个函数地址:011e7868
baseClass function a()
请按任意键继续. . .

下面是虚函数表的详细情况:

图1
(int*)(&cBase) //强制转换cBase对象在内存中的内容为int*[0x0031fce4],它指向cBase对象的第一个成员的地址,即虚函数表的地址[0x011e7868]
(int*)*(int*)(&cBase) //对虚函数表的地址进行解引用,*(int*)(&cBase)指向的地址就是虚函数表的地址,然后转换为int*,它指向虚函数表的第一个成员,即_vfptr[0] ,地址为[0x011e121c]


下面是关于基类地址,以及虚函数表、基类成员、派生类成员的详细分布:


图2

从上面的图中我们可以看到,派生类的成员a()覆盖了基类的成员a()
C++中的重载、覆盖、隐藏
1.重载从最简单的角度来讲只发生在对象内部,对象内部同名的函数,但是参数个数或参数类型不同;

2.覆盖就是上面图中标示的那种情况;

3.当派生类和基类的函数同名,而且基类同名函数前virtual修饰符,基类的同名函数被隐藏;

“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆) 。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) 。


内联函数:
关键字inline,功能类似宏替换,具有函数的结构,在编译时刻根据函数名来替换成对应的代码,在代码量小的重复次数多的情况下,比较高效.不是适合复杂代码,同时是否能够实现内联的功能,具体要依赖编译器,有可能编译器根据实际情况当成普通函数来处理.
静态成员函数:
静态成员在同类对象中只有一个实例,因此可以用来统计同类对象的计数;
静态成员函数独立于类对象,因此即使类对象不存在,静态成员函数依然可以被调用,静态成员函数只能调用静态成员变量在这样的情况下.

实例:Singleton模式,保证一个类只有一个实例对象,同时使该实例只有一个全局访问点.


C++对象属性:public, protected, private

共有继承,基类的公共成员和保护成员可以作为其派生类的公共成员和保护成员.派生类无法访问基类的私有成员.

私有继承,基类的共有成员和私有成员都作为其派生类的私有成员.基类的成员只能由直接派生类来访问,不能再往下继承。

保护继承,基类的共有成员和保护成员都作为其派生类的保护成员,基类的共有成员和保护成员只能被其派生类成员和友元函数访问.

分享到:
评论

相关推荐

    类、构造函数、析构函数.pdf

    如果未声明析构函数,编译器也会提供一个默认的公共析构函数。 拷贝构造函数是一种特殊的构造函数,用于通过已存在的对象创建新对象。拷贝构造函数的特点: - 名称与类名相同。 - 接受一个对同类对象的引用作为参数...

    C++高级参考手册,讲解C++ 语法,函数重载,构造函数等

    C++是一种强大的面向对象编程语言,它以其灵活性、效率和丰富的特性而闻名。在C++高级参考手册中,深入探讨了C++的核心概念和技术,帮助开发者掌握更高级的编程技巧。以下是对标题和描述中涉及知识点的详细解释: 1...

    c++单项选择题.pdf

    C++是一种强大的面向对象编程语言,它在C语言的...以上是对题目中涉及的C++知识点的详细解释,涵盖了类、对象、构造函数、析构函数、访问控制、虚函数、多态性等方面的知识。理解这些概念是掌握C++面向对象编程的基础。

    华为C++笔试题全部汇总

    总结:这些题目涵盖了C++中的虚函数、析构函数的虚性、内联函数、对象构造与析构、多态性、引用和值传递的区别、虚表指针、以及重载操作符的多态行为等多个核心概念。在实际编程中,理解并掌握这些知识点对于编写...

    (1)C++语言对C语言做了很多改进.docx

    C++支持构造函数的重载,但析构函数不能重载。 4. 内联函数:内联函数是为了优化程序性能,编译器在编译时将函数体展开,减少函数调用开销。在类内定义的函数默认为内联,但不是所有内联函数都必须在类体内定义。 ...

    C++的具体实现 函数类

    【标题】:“C++的具体实现 函数类” ...以上是C++类函数的一些基本概念,实际编程中还有更多高级特性,如纯虚函数、内联函数、静态_cast等。了解并熟练掌握这些知识点对于深入学习C++编程至关重要。

    多态性 面向对象程序设计\c++考试题

    多态性面向对象程序设计C++考试题 多态性是面向对象程序设计的核心...在C++考试题中,多态性是非常重要的知识点,需要考生能够熟练地掌握多态性的概念和实现方法,包括运算符重载、虚函数、虚析构函数和纯虚函数等。

    C++选择题和判断题.docx

    18. 构造函数:构造函数可以设置缺省参数,不能被继承,不能初始化静态数据成员,也不能声明为虚函数,选项A正确。 19. 友元:友元可以访问类的私有和保护成员,提高了代码的灵活性,但可能破坏封装性。 以上是C++...

    C++试题.pdf

    《面向对象程序设计 C++》期末考试试卷涵盖了C++编程语言的...以上知识点覆盖了C++语言的核心概念,包括面向对象特性、输入输出、函数、类的成员、继承、多态、友元和静态成员等。理解和掌握这些内容是学习C++的基础。

    C++复习题及参考答案.pdf

    C++是一种强大的面向对象的编程语言,它包含了丰富的特性,如类、构造函数、析构函数、重载运算符、继承、多态等。以下是对题目中涉及的一些知识点的详细解释: 1. **类和成员访问权限**: - 类的定义由说明部分...

    C++期末复习(选择、判断与填空).pdf

    这些题目涵盖了C++编程语言的多个核心概念,包括函数原型、对象通信、析构函数、模板、友元、内联函数、派生类与基类的关系、虚函数、构造函数和析构函数的特性、访问控制以及多态性等。下面是对这些知识点的详细...

    C++模拟题5.docx

    15. **类的成员函数**:构造函数、析构函数和拷贝构造函数是类的成员函数,而友元函数不是。 16. **继承的作用**:继承允许创建新类(派生类),这些新类基于已存在的类(基类),实现数据封装和代码复用。 17. **...

    最新C++全套视频教程及实战开发

    6、构造函数、析构函数、拷贝构造函数、函数重载 7、 对象数组、this指针、 枚举 8、静态成员、静态成员函数 9、对象成员 10、友元 11、 封装、继承、多继承、多态 12、虚函数、纯虚函数、抽象类、虚析构函数等

    C++经典面试题库 附带参考答案.docx

    7. 缺省函数:C++编译器会自动生成默认构造函数、拷贝构造函数、析构函数和赋值运算符。它们分别用于对象创建、对象复制、对象销毁和对象赋值。 8. 拷贝构造函数调用:主要在对象初始化、函数参数传递和函数返回值...

    C++面向对象程序设计模拟试题五.doc

    C++是一种面向对象的编程语言,它支持类、对象、构造函数、析构函数、重载函数、内联函数、虚函数、静态成员、模板等特性。以下是对题目中涉及知识点的详细解释: 1. **构造函数与析构函数**: - 构造函数是用于...

Global site tag (gtag.js) - Google Analytics