C++中构造函数、析构函数和虚函数
来源:互联网 发布:caffe 继续训练 编辑:程序博客网 时间:2024/06/05 02:28
1.类
类的三个新特征:友元(friend)、可变成员和静态成员。
友元:允许一个类将对其非公有成员的访问权授予指定的函数或类。
2.new与delete
new时做了三件事:①分配内存空间。②调用构造函数。③返回对象指针。
delete时做了两件事:①调用析构函数。②释放内存空间。
3.构造函数
1)实参决定使用哪个构造函数。
2)构造函数自动执行。
3)先调用基类构造函数,再调用子类构造函数。
4)常见类型(无参、拷贝、重载)及调用过程。与等号运算符重载区分。
例1:
#include <iostream>using namespace std;class Complex{ private : double m_real; double m_imag; int id; static int counter;public: // 无参数构造函数 Complex(void) { m_real = 0.0; m_imag = 0.0; id=(++counter); cout<<"Complex(void):id="<<id<<endl; } // 一般构造函数(也称重载构造函数) Complex(double real, double imag) { m_real = real; m_imag = imag; id=(++counter); cout<<"Complex(double,double):id="<<id<<endl; } // 复制构造函数(也称为拷贝构造函数) Complex(const Complex & c) { // 将对象c中的数据成员值复制过来 m_real = c.m_real; m_imag = c.m_imag; id=(++counter); cout<<"Complex(const Complex&):id="<<id<<" from id="<<c.id<<endl; } // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象 Complex(double r) { m_real = r; m_imag = 0.0; id=(++counter); cout<<"Complex(double):id="<<id<<endl; } ~Complex() { cout<<"~Complex():id="<<id<<endl; } // 等号运算符重载 Complex &operator=( const Complex &rhs ) { if ( this == &rhs ) { return *this; } this->m_real = rhs.m_real; this->m_imag = rhs.m_imag; cout<<"operator=(const Complex&):id="<<id<<" from id="<<rhs.id<<endl; return *this; }};int Complex::counter=0;Complex test1(const Complex& c){ return c;}Complex test2(const Complex c){ return c;}Complex test3(){ static Complex c(1.0,5.0); return c;}Complex& test4(){ static Complex c(1.0,5.0); return c;}int main(){ Complex a,b;//各调用一次Complex(void)构造函数 // 下面函数执行过程中各会调用几次构造函数,调用的是什么构造函数? Complex c=test1(a);//①test1()②拷贝构造函数 Complex d=test2(a);//①拷贝②test2()③拷贝Complex h(c);//拷贝Complex i = h; //拷贝,相当于Complex i(h);c=h;//等号运算符 b = test3();//①一般②拷贝③等号 b = test4();//①一般②等号 Complex e=test2(1.2);//①类型转换②拷贝 Complex f=test1(1.2);//①类型转换②拷贝 Complex g=test1(Complex(1.2));//①类型转换②拷贝}注:
①等号:对象已初始化;拷贝:对象未初始化。
<pre name="code" class="cpp">Complex i = h;//调用拷贝;i = h;//调用等号②
<span style="background-color: rgb(255, 255, 0);">Complex c = test1(a);//调用1次构造函数</span>上式结果等价于
<span style="background-color: rgb(255, 255, 0);">Complex c1 = test1(a);//调用1次Complex c = c1;//调用1次,总共2次,因为多了中间变量c1.</span>
4.析构函数
牢记析构函数的调用顺序(与构造函数的调用顺序相反)
什么时候调用析构函数?
1)对象生命周期结束,被销毁时;
2)delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
3)对象i是对象o的成员,o的析构函数被调用时,对象的析构函数也被调用。
例2:
class A{public:A(){cout<<"constructing A"<<endl;}~A(){cout<<"destructing A"<<endl;}private:int a;};class B:public A{public:B(){cout<<"constructing B"<<endl;}~B(){cout<<"destructing B"<<endl;}private:int b;};void main(){B b;}输出结果:
说明:先调用父类构造方法,再调用子类构造方法。
上述代码说明了一件事:析构函数的调用顺序与构造函数的调用顺序相反。
例3:
main()方法改为:
void main(){A *a = new B;delete a;}输出结果:
例4:
class A{public:A(){cout<<"constructing A"<<endl;}virtual ~A(){cout<<"destructing A"<<endl;}private:int a;};class C{public: C() { cout<<"constructing C"<<endl; } ~C() { cout<<"destructing C"<<endl; } private: int c;};class B:public A{public:B(){cout<<"constructing B"<<endl;}~B(){cout<<"destructing B"<<endl;}private:int b;C c;};void main(){B b;}输出结果:
5.虚函数-virtual function
多态、虚函数表(内存中实例、最前面、地址表、父子类在内存中函数位置)、
5.1 定义
C++中虚函数的作用主要是实现了多态的机制。多态,简言之就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。
5.2 虚函数表
虚函数(virtual function)是通过一张虚函数表(virtual table)来实现的,简称V-Table。该表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其能真实地反映实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中。
C++中,编译器必须要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
代码如下:
#include <iostream>using namespace std;class Base{public:virtual void f(){cout<<"f()"<<endl;}virtual void g(){cout<<"g()"<<endl;}virtual void h(){cout<<"h()"<<endl;}};void main(){Base b;}内存结构如下:
一般覆盖(无虚函数覆盖)
假设有如下所示的一个继承关系:
在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,其虚函数表如下所示:对应实例:Derive d;的虚函数表如下:
可看到下面几点:
1)虚函数按照其声明顺序存在于表中。
2)父类的虚函数在子类的虚函数前面。
一般继承(有虚函数覆盖)
虚函数如下所示:
从表中可看到以下几点
1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。
多重继承(无虚函数覆盖)
从上面可看到以下几点:
1)每个父类都有自己的虚表
2)子类的成员函数被放到了第一个父类的表中
多重继承(有虚函数覆盖)
注:
一、::是运算符中等级最高的,表示作用域和所属关系,它分为三种:
1)global scope(全局作用域符),用法(::name)
2)class scope(类作用域符),用法(class::name)
3)namespace scope(命名空间作用域符),用法(namespace::name)
例5:类作用域
#include <iostream>using namespace std;class First{public:int memi;double memd;};class Second{public:int memi;double mend;}void main(){First f;Second s = f;}
提示错误:不存在用户定义的从"First"到"Second"的适当转换。每个类都定义了自己的新作用域和唯一的类型。两个不同的类具有两个不同的类作用域。即使两个类具有完全相同的成员列表,它们也是不同的类型。每个类的成员不同于任何其他类的成员。
结束语
参考:
http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html
http://blog.csdn.net/weizhee/article/details/562833
http://blog.csdn.net/wuchuanpingstone/article/details/6742465
- C++中构造函数、析构函数和虚函数
- 《Effective C++》不要在构造函数和析构函数中调用虚函数
- 构造函数和析构函数【c++】
- [c++]构造函数和析构函数
- 【C#】构造函数和析构函数
- 【C++】构造函数和析构函数
- 【C++】构造函数和析构函数
- C++:构造函数和析构函数
- <C++>析构函数、虚析构函数和构造函数
- 构造和析构函数中不允许有虚函数
- 构造和析构函数中绝不调用虚函数
- C++:构造函数中调用虚函数
- C++:构造函数中调用虚函数
- 【C/C++】构造函数和析构函数
- C/C++——构造函数和析构函数
- 【C++】构造函数 析构函数 虚函数
- C/C++——构造函数、复制构造函数和析构函数的执行时刻
- 构造函数和析构函数,在C++中能否声明为虚函数?构造函数和析构函数里面能否调用虚函数?
- java实现链表的反转
- PHP 伪静态技术原理以及突破原理实现介绍
- JDBC数据源连接池的配置
- 经纬度地址转换的方法集合(Python描述)
- Linux中的帮助文档
- C++中构造函数、析构函数和虚函数
- Palindromic Squares(模拟)(USACO)
- php之微信开发获取用户openid的方法
- 基于php伪静态的实现详细介绍
- 数
- 【ligth-oj】1225 - Palindromic Numbers (II)(水)
- Spring与Quartz的整合实现定时任务调度
- Ubuntu16.04 安装caffe
- 利用树莓派组建支持迅雷离线下载的NAS