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

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

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); 这条语句。

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

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

    说明:如果看不懂的童鞋,可以直接跳到最后看总结,再回头看上文内容,如有不对,请指出~ 环境:visual studio 2013(编译器优化关闭) 源代码 下面的源代码修改自http://blog.c ...

  2. 默认初始化&拷贝初始化&直接初始化&值初始化&列表初始化

    一.各种初始化的形式 /* 定义变量形式一:不指定初始值 */ int a; // 默认初始化 /* 定义变量形式二:指定初始值 */ int b = 1; // 拷贝初始化 int b(1); // ...

  3. go语言初始化结构体指针

    go语言初始化结构体指针 head:=&ListNode{} 或者 head:=new(ListNode)

  4. C++ 拷贝构造函数、拷贝赋值运算符、析构函数

    每一次都会忘,做个笔记吧.想到哪里写到哪里. 拷贝构造函数 第一个参数必须是自身类类型的引用,且任何额外参数都有默认值.(为什么必须是引用?见后解释) 合成拷贝构造函数:如果我们没有为一个类定义拷贝构 ...

  5. C++ 拷贝控制和资源管理,智能指针的简单实现

    C++ 关于拷贝控制和资源管理部分的笔记,并且介绍了部分C++ 智能指针的概念,然后实现了一个基于引用计数的智能指针.关于C++智能指针部分,后面会有专门的研究. 通常,管理类外资源的类必须定义拷贝控 ...

  6. 拷贝构造函数 & 拷贝赋值运算符

    一.拷贝构造函数 1. 形式 class A { public: // ... A(const A &); // 拷贝构造函数 }; 2. 合成拷贝构造函数 编译器总会为我们合成一个拷贝构造函 ...

  7. [LeetCode] Copy List with Random Pointer 拷贝带有随机指针的链表

    A linked list is given such that each node contains an additional random pointer which could point t ...

  8. C++中关于指针初始化和使用NULL的理解

    1.严禁使用未被初始化的指针:C++创建指针的时候,只分配存储地址的内存,并不会分配存储数据的内存,所以指针可能指向任何位置. (1)使用解除运算符(*)之前,一定要对指针初始化,否则若声明的指针刚好 ...

  9. (原)C++中指针不初始化就传递的问题

    C++中指针的使用.以前在使用指针之前都会初始化.今天没有初始化,然后指针传递后没有内容(testptrnoret),后来发现返回指针的话(testptrret),就可以了. // testptr.c ...

随机推荐

  1. java读取properties配置文件的方法

    app.properties mail.smtp.host=smtp.163.com mail.transport.protocol=smtp import java.io.InputStream; ...

  2. cpu缓存与多线程

    一.cpu缓存结构 CPU速度远高于内存(即如果只考虑CPU和内存因素,程序的性能常常受到内存访问速度的限制,内存访问和运行),为了协调CPU和内存在速度上的差异,在CPU中增加了高速缓存.和计算机存 ...

  3. noip2016酱油记day1

    真的是noip2016酱油记了. t1模拟,应该可以过. t2用了个简单的桶瞎搞,估计剩50pt了. t3直接不会写. 心好累... 考的分数肯定没去年高. 但不论如何,明天正常发挥就好. 正常发挥下 ...

  4. jQuery用户从服务器端注册登录

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. selenium+python笔记4

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ @desc: 使用unittest组织用例 ""& ...

  6. 《Java程序设计》实验四 实验报告

    实验四 Android开发基础 实验内容 XP基础 XP核心实践 相关工具 实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim编辑器> 课程 ...

  7. 《javascript高级程序设计》第七章 递归recursion

    7.1 递归7.2 闭包 7.2.1 闭包与变量 7.2.2 关于this 对象 7.2.3 内存泄漏 7.3 模仿块级作用域7.4 私有变量 7.4.1 静态私有变量 7.4.2 模块模式 7.4. ...

  8. (30)odoo中的快捷标签

    * 快捷标签   提供快捷标签是为了简化代码的编码,把复杂的工作封装化   * 找到封装化的源码:  openerp/tools/convert.py   xml_import      self._ ...

  9. Ajax发送和接收请求

    首先Ajax的不刷新页面提交数据 基本上浏览器能接收的信息,Ajax都可以接收,ex:字符串,html标签,css标签,xml格式内容,json格式内容等等..... <script> / ...

  10. 学习manacher(最长公共回文串算法)

    给定一个字符串求出其中最长个公共回文串. 举列子: abab   -->回文串长度为2 以前的算法诸如: 扩展kmp求法过于麻烦,看到有一篇博文(http://leetcode.com/2011 ...