赋值运算符、拷贝初始化和this指针

一、赋值运算符和拷贝构造函数(重载技术)

赋值运算符和拷贝构造函数有编译器默认提供,但如果想做更复杂的事,需要重载。

1.下面用一个简单的例子先区分一下赋值运算符和拷贝构造函数:

#include<iostream>

using namespace std;

class  alpha

{

public:

    alpha():data(0) {}       //没有参数的构造函数

    alpha(int d):data(d) {}  //一个参数的构造函数

    void diplay()            //显示数据

    {

        cout<<data<<endl;

    }

    alpha(alpha& a)          //重载拷贝构造函数

    {

        data=a.data;

        cout<<"copy constructor invoked! "<<endl;

    }

    alpha operator = (alpha& a) //重载赋值运算符

    {

        data=a.data;

        cout<<"assignment operator invoked! "<<endl;

        return alpha(data);  //单参数的构造函数

    }

private:

    int data;

};

int main()

{

    alpha a1(32);

    alpha a2;     //无参数构造函数

    a2=a1;        //赋值运算符

    a2.diplay();

    alpha a3=a1;  //拷贝构造函数

    a3.diplay();

    alpha a4(a1);  //拷贝构造函数

    a4.diplay();

    return 0;

}

1)首先区别什么是赋值,什么是初始化。

2)程序中重载采用了引用传递,原因:①众所周知,用值传递的参数将在要传递的函数内产生一个副本,没有例外。如果这个对象很大,则副本就会浪费汗多空间。②在某些情况下可能想记录对象的数目。如果编译器在使用赋值运算符时,每次产生一个额外的对象,这样就可能见到比医疗多得多的对象。而引用传递有助于避免创建过多的对象。

3)return alpha(data); 返回值是重载函数所在的对象的一个副本,而不是同一个对象。返回的值可使它将运算符=串联起来:a3=a2=a1;

4) 然而alpha operator = (alpha& a)中值的返回与值的参数一样的缺点:浪费内存空间。但是使用引用来返回函数的值如alpha& operator = (alpha& a) 这样行吗?

回答是不行的:在函数内部创建的非static的局部变量,在函数返回时即被销毁,引用所返回的值,仅仅是实际希望返回值得地址,是没有意义的,甚至会出现严重错误。(在这里我们可以用this指针解决这个问题,后文会解释)

注意:赋值运算符是唯一一个不能继承的运算符。如果在基类重载了赋值运算符,那么在任何派生类中都不能再重载同一函数。

2.看函数的几种情况

1)函数参数

void func(alpha);  //以值传递对象

func(a1);      //函数调用

这时拷贝构造函数将会被调用来创建一个对象a1的副本,并将副本交给函数func()操作。(当然引用或者指针就不会调用拷贝构造函数)

2)函数返回值

alpha func()  //函数声明

a2=func()    //函数调用

在这里:首先,程序调用拷贝构造函数来穿件一个func()返回值的副本;

然后,这个值再被赋给a2(调用赋值运算符)。

3)拷贝构造函数为alpha(alpha& a) ,为什么不是alpha(alpha a)的形式?

答:拷贝构造函数必须使用引用,否则会报告内存溢出。(因为如果参数用值来传递,就需要创建一个该值的副本。如何创建副本呢?使用拷贝构造函数,但是原函数本来就是要定义拷贝构造函数,这样不断调用自己你说会有什么结果!~

二、this指针

每一个对象的成员函数都可以访问一种神奇的指针,即指向该对象本身的this指针,因而在任何对象中都可找到所属对象的自身地址。

#include<iostream>

using namespace std;

class  alpha

{

public:

    alpha():data(0) {}      

    alpha(int d):data(d) {} 

    void display()           

    {

        cout<<data<<endl

            <<this->data<<endl;

    }

    //alpha operator = (alpha& a) //重载赋值运算符

    //{

    //  data=a.data;

    //  cout<<"assignment operator invoked! "<<endl;

    //  return alpha(data);  //单参数的构造函数

    //}

    alpha& operator = (alpha& a) //重载赋值运算符

    {

        data=a.data;

        cout<<"assignment operator invoked! "<<endl;

        return *this;     //通过this指针返回值

    }

private:

    int data;

};

int main()

{

    alpha a1(37);

    a1.display();  //直接输出和this->data的输出结果是一样的

    alpha a2,a3;

    a3=a2=a1;    //调用赋值运算符

    cout<<"a2=";a2.display();

    cout<<"a3=";a3.display();

    return 0;

}

注意实例中的:使用this指针返回值。(从成员函数和重载运算符返回值,this指针是一个更实用的用法)

1)this指针指向的是该成员函数所属的对象,所以*this就是这个对象本身。通常实用引用和this指针从重载赋值运算符返回数据,从而避免创建额外的对象。

2)必须注意:this指针在静态成员函数中是无效的,因为静态成员函数不属于任何特定的对象。

三、dynamic_cast和typeid

这两个功能通常使用在有很多类都由一个基类派生的情况下。为了使动态类型转换能够工作,基类必须是多态的(也就是说至少包含一个虚函数)。

1.dynamic_cast可以改变指针类型

#include<iostream>

#include<typeinfo>

using namespace std;

class Base

{

public:

    Base() {}

    virtual void vertfunc()  //要想用dynamic_cast,基类必须是多态类型

    {}

    Base(int b):ba(b){}

    void show()

    {

        cout<<"Base: ba="<<ba<<endl;

    }

protected:

    int ba;

};

class derv1:public Base

{};

class derv2:public Base

{

public:

    derv2():Base() {}

    derv2(int b,int d):Base(b),da(d) {}

    void show()

    {

        cout<<"derv2: ba="<<ba<<" da="<<da<<endl;

    }

private:

    int da;

};

bool isDerv1(Base* pUnknown)

{

    derv1* pderv1;

    if (pderv1=dynamic_cast<derv1*>(pUnknown))

        return true;

    else

        return false;

}

int main()

{

    derv1* d1=new derv1;

    derv2* d2=new derv2;

    if (isDerv1(d1))

        cout<<"d1是类derv1的一个对象"<<endl;

    else

        cout<<"d1不是类derv1的一个对象"<<endl;

    if (isDerv1(d2))

        cout<<"d2是类derv1的一个对象"<<endl;

    else

        cout<<"d2不是类derv1的一个对象"<<endl;

    Base* pbase=new Base(10);

    derv2* pderv=new derv2(22,33);

    pbase->show();pbase=dynamic_cast<Base*>(pderv); pbase->show(); //派生类到基类

    pbase=new derv2(34,32);

    pderv->show(); pderv=dynamic_cast<derv2*>(pbase); pderv->show();  //基类到派生类

    return 0;

}

2.typeid可以得到未知的对象类型信息

如上面的实例中最后加上:

cout<<typeid(*pbase).name()<<endl;

运行程序会显示class derv2,因为pbase=new derv2(34,32); 这条语句。

时间: 2024-10-08 08:16:19

赋值运算符、拷贝初始化和this指针的相关文章

【原创】c++拷贝初始化和直接初始化的底层区别

说明:如果看不懂的童鞋,可以直接跳到最后看总结,再回头看上文内容,如有不对,请指出~ 环境:visual studio 2013(编译器优化关闭) 源代码 下面的源代码修改自http://blog.csdn.net/ljianhui/article/details/9245661 1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 class ClassTest 5 { 6 public: 7 Cl

直接初始化&amp;拷贝初始化&amp;值初始化

一.变量定义&初始值 0. 变量定义的形式 //形式一 int a; //形式二 int b = 1; 1. 初始值&初始化 对象被初始化:对象在创建时获得了一个特定的值 2. 初始化&赋值 初始化变量的一种方式:用等号=来初始化变量 这种方式让人误以为初始化是赋值的一种,其实不然,初始化不是赋值 初始化:创建变量时赋予其一个初始值 赋值:把对象的当前值擦除,而以一个新值来替代 3. 列表初始化 int a = {0}; int a{0}; 4. 默认初始化 定义变量时没有指定初值

c++拷贝构造函数、赋值运算符=重载、深拷贝与浅拷贝

 关键词:构造函数,浅拷贝,深拷贝,堆栈(stack),堆heap,赋值运算符 摘要: 在面向对象程序设计中,对象间的相互拷贝和赋值是经常进行的操作. 如果对象在申明的同时马上进行的初始化操作,则称之为拷贝运算.例如: class1 A("af"); class1 B=A; 此时其实际调用的是B(A)这样的浅拷贝操作. 如果对象在申明之后,在进行的赋值运算,我们称之为赋值运算.例如: class1 A("af"); class1 B; B=A; 此时实际调用的类

赋值运算符重载和拷贝构造函数 AND 浅拷贝与深拷贝

赋值运算符重载: 是用一个已经存在的对象去给另一个已经存在并初始化(调用过构造函数)的对象进行赋值. 拷贝构造函数:其实本质还是构造函数,用一个已存在的对象去构造一个原先不存在的对象. string a("hello"); string b("world"); string c =a ;   //拷贝构造函数 c = b;           //调用赋值函数 一般来说是在数据成员包含指针对象的时候,应付两种不同的处理需求的 一种是复制指针对象,一种是引用指针对象

c++类的拷贝、赋值与销毁(拷贝构造函数、拷贝赋值运算符析构函数)

拷贝构造函数     如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数. 拷贝构造函数第一个参数必须是一个引用类型.此参数几乎总是一个const的引用.拷贝构造函数在几种情况下都会被隐式地使用.因此,拷贝构造函数通常不应该是explicit的. 合成拷贝构造函数 与合成默认构造函数不同,即使我们定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数. 对某些类来说,合成拷贝构造函数用来阻止我们拷贝该类类型的对象.而一般情况,合成的拷贝构造函数

拷贝构造函数 &amp; 拷贝赋值运算符

一.拷贝构造函数 1. 形式 class A { public: // ... A(const A &); // 拷贝构造函数 }; 2. 合成拷贝构造函数 编译器总会为我们合成一个拷贝构造函数,即使我们定义了其他构造函数. 3. 拷贝构造函数不应该是explicit的 拷贝构造函数在必要时可以被隐式地使用. 4. 拷贝初始化 ①用=定义对象 string s1 = s; string s2 = "hello"; string s3 = string(10, 'c'); ②传递

构造函数、初始化列表来初始化字段、析构函数、拷贝构造函数

#include <iostream> using namespace std; class Line { public: int getLength( void ); Line( int len ); // 简单的构造函数 Line( const Line &obj); // 拷贝构造函数 ~Line(); // 析构函数 private: int *ptr; }; // 成员函数定义,包括构造函数 Line::Line(int len) { cout << "

指针初级(定义, 初始化, 重指向, 注意事项)

大家好, 今天我们来学习C语言中的指针. 可能大家都听过这么一句话: C语言中其它我学得都很好, 就是指针这一块学得不是太明白, 那么你不如告诉我你没有学过C语言. 这么一句话突出了指针这个概念在C语言当中的重要性. 还有的同学说, 我听别从说C语言当中的指针好难, 特别难理解, 很难学会.那么我告诉大家的是, 指针没有什么难的地方, 那只是你们潜意识这么认为的, 通过今天这节课的学习, 我相信大家都能学会指针, 并且会用指针. 好, 下面开始指针的学习. 在学指针之前, 我先给大家普及一下,

C Language Study - 错误的指针初始化方式

本文测试指针初始化的方式: 错误初始化方式一: int* p = NULL;//此句等于p = NULL;将指针指向了NULL这个地址,(NULL=0x0) *p = 0x10;//试图访问0x0内存,被拒绝 错误初始化方式二: int* p;//定义的时候未进行初始化,此时指针指向一块未知的内存 *p = 0x10;//试图访问未知内存,被拒绝 那么指针到底如何初始化? 初始化方式参考一: 首先定义一个相应类型的变量,我们称之为指针初始化变量,它的作用 就是用来进行指针初始化的,或者程序中可以