zoukankan      html  css  js  c++  java
  • c++学习笔记一

    定一个头文件person.h包含类的声明:每行后面的注释是学习过程中的体会与思考

    #include<iostream>

    #ifndef PERSON_H_
    #define PERSON_H_


    class Person
    {
    private:
     int ID;//只有静态的常量数据成员才可以在类中初始化,与C#不一样
     std::string Name;
     int Age;
     double Money;
     char * Address;//定义一个字符串指针成员
     static int PersonNum;//定义一个静态变量记录对象的个数,静态变量要在源文件中初始化,如果没初始化会有错误,为什么?
    public:  
        Person();//默认的构造函数
     Person(int id,std::string name,int age,double m,const char* s);//带有参数的构造函数
     Person(const Person & s);//定义复制构造函数
     Person & operator=(const Person &p);//重载=操作符,实现深度复制
     virtual ~Person();//析构函数

     virtual void playway();//定义一个虚函数,通过虚函数实现多态
     void GetName();
     void SetPerson(int id, std::string name,int age,double m);
     const Person & ComparePerson(const Person& p) const;//比较两个person的钱的大小,返回钱多的对象,括号后面的const表面函数不会修改被隐式的访问对象,参数为引用传递
      const Person & ComparePersonPassValue(Person p) const;//比较两个person的钱的大小,返回钱多的对象,括号后面的const表面函数不会修改被隐式的访问对象,参数为值传递

     Person operator*(double m)const;//通过成员函数来重载操作符,会隐式的传递默认的调用对象作为第一个参数,即新对象=对象*值,如果要执行值*对象,就要用下面的友元函数解决了
     friend Person operator*(double m,const Person & p)//通过友元函数解决值*对象的问题,注意第一个参数为double,第二个参数为Person类型引用,同时函数定义为内联形式,内联函数可以直接在头文件中定义
     {
      return p*m;//会调用operator*(double m)const这个成员函数
     }
     friend std::ostream& operator<<(std::ostream& os,const Person &p);//友元函数重载操作符<<,便于输出类型,因为在一般情况下cout<<只能输出一些标准类型的变量

    };//类声明后,有分号,与C#不同

    class ManOrWomenPerson:public Person //定义ManPerson继承于Person
    {
    private:
      unsigned int Sex;//0 标示男人,1表示女人
    public:
      ManOrWomenPerson(int id,std::string name,int age,double m,const char* s,unsigned int sex);//为继承的类定义一个新的带参数的构造函数
         ManOrWomenPerson(const Person & p,unsigned int sex);//为继承类定义一个复制构造函数,想想复制构造函数在什么时候被调用
         ~ManOrWomenPerson();//析构函数
      virtual void playway();//定义一个虚函数
      void ShowSex();//定义一个返回性别的函数
    };//这个地方分号要注意,c#中没有


    #endif

    Person.cpp类的实现

    #include<iostream>
    #include<string>
    #include "person.h"
    using std::cout;

    int Person::PersonNum=0;

    void Person::GetName()
    {
     cout<<"Name:"<<Name<<std::endl;
     cout<<"Money:"<<Money<<std::endl;
    }

    Person::Person()
    {
     cout<<"调用了默认的构造函数"<<std::endl;
     PersonNum++;
     cout<<"对象个数为:"<<PersonNum<<std::endl;
     
    }

    Person::Person(int id,std::string name,int age,double m,const char* s)
    {
     cout<<"调用了带有参数的构造函数"<<std::endl;
     ID=id;
     Name=name;
     Age=age;
     Money=m;

     Address=new char[std::strlen(s)+1];//通过new分配新的空间
     std::strcpy(Address,s);
     PersonNum++;
     cout<<"对象个数为:"<<PersonNum<<std::endl;
    }

    Person::Person(const Person & s)
    {
     cout<<"调用了基类Person复制构造函数进行深度复制"<<std::endl;
     ID=s.ID+1;
     Name=s.Name+"1";
     Age=s.Age+1;
     Money=s.Money+1;

     Address=new char[std::strlen(s.Address)+1];//通过new分配新的空间
     std::strcpy(Address,s.Address);
     PersonNum++;
     cout<<"对象个数为:"<<PersonNum<<std::endl;
    }

    Person & Person::operator=(const Person& p)//重载赋值操作运算符
    {
     if(this==&p) //判断是否是自己给自己赋值
      return *this;

     cout<<"调用了Person重载的赋值=函数进行深度复制"<<std::endl;
     ID=p.ID+2;
     Name=p.Name+"2";
     Age=p.Age+2;
     Money=p.Money+2;

     /*delete [] Address; 删除一个指向不明确的指针时会报错*/
     Address=new char[std::strlen(p.Address)+1];//通过new分配新的空间
     std::strcpy(Address,p.Address);
     return *this;
    }

     Person::~Person()
    {
     PersonNum--;
     delete []Address;//释放指针所指向的内存空间及内容,与new对应,析构函数中一定要有这个,否则对象释放后,却没有释放对象中得指针成员所指向的内容,造成内存泄露
     cout<<"调用了基类类Person析构函数释放了对象:"<<Name<<std::endl;
     cout<<"对象个数为:"<<PersonNum<<std::endl;
    }

    void Person::SetPerson(int id,std::string name,int age,double m)
    {
     ID=id;
     Name=name;
     Age=age;
     Money=m;
    }

    const Person& Person::ComparePerson(const Person & p) const //比较两个person的钱的大小,返回钱多的对象,括号后面的const表面函数不会修改被隐式的访问对象,参数为引用传递
    {
     if(p.Money>Money)
     { 
      return p;
     }
     else
     { 
      return *this;//this 是指向本对象的一个指针,*this表示对象本身,返回类型为引用,表示返回的不是对象的拷贝,而是对象本身。
     }
    }

    const Person& Person::ComparePersonPassValue(Person p) const //比较两个person的钱的大小,返回钱多的对象,括号后面的const表面函数不会修改被隐式的访问对象,参数为值传递
    {
     if(p.Money>Money)
     { 
      return p;//这里返回局部变量的地址作为引用,是有危险的,因为局部变量使用后就消失了,引用的对象将不会存在
     }
     else
     { 
      return *this;//this 是指向本对象的一个指针,*this表示对象本身,返回类型为引用,表示返回的不是对象的拷贝,而是对象本身。
     }
    }

    Person Person::operator*(double mult)const //定义成员函数重载的操作符,返回一个新的Person对象
    {
     Person p; //局部变量类,执行完return语句后,跳出函数体,析构函数将被调用
     p.Money=this->Money*mult;//或者写为Money*mult,this作为隐式参数传递进来的,成员函数可以直接访问类的私有变量p.Money
     return p;
    }

    std::ostream & operator<<(std::ostream& os,const Person &p)//os参数是对cout的引用,重载了<<操作符
    {
     os<<"对象姓名:"<<p.Name<<" 财产:"<<p.Money<<" 地址:"<<p.Address<<std::endl;
     return os;
    }

    void Person::playway()
    {
     cout<<"调用了基类Person的的动作函数"<<std::endl;
     cout<<"爱好:"<<"还没有"<<std::endl;
    }

    ManOrWomenPerson::ManOrWomenPerson(int id,std::string name,int age,double m,const char* s,
                 unsigned int sex):Person(id,name,age,m,s)//派生类的带参数的构造函数,会先调用基类的构造函数Person(id,name,age,m,s)在调用此构造函数,构造函数不能继承
    {
     Sex=sex;
     cout<<"调用了派生类ManOrWomenPerson的带参数的构造函数"<<std::endl;
    }

    ManOrWomenPerson::ManOrWomenPerson(const Person & p,unsigned int sex):Person(p)//派生类的复制构造函数,会先调用基类的复制构造函数,构造函数不能继承
    {
     cout<<"调用了派生类ManOrWomenPerson的复制构造函数"<<std::endl;
     /*Sex=sex; 这句什么时候会被调用,有疑问???? */
    }

    void ManOrWomenPerson::ShowSex()
    {
     if(Sex==1)
      cout<<"性别:"<<"男"<<std::endl;
     else
        cout<<"性别:"<<"女"<<std::endl;
    }

    ManOrWomenPerson::~ManOrWomenPerson()//派生类的析构函数,析构函数不能继承
    {
      cout<<"调用了派生类ManOrWomenPerson的析构函数"<<std::endl;
    }

    void ManOrWomenPerson::playway() //虚拟函数,申明的时候有virtual修饰,定义的时候不需要
    {
     cout<<"调用了派生类ManOrWomenPerson的的动作函数"<<std::endl;
     if(Sex==1)
      cout<<"爱好:"<<"打游戏"<<std::endl;
     else
        cout<<"爱好:"<<"打麻将"<<std::endl;
    }


    main函数的实现:

     /*
     Person person1;//调用默认的构造函数
     person1.SetPerson(1,"xiaoming",30,1000000);
     person1.GetName();//通过公用方法访问类的私有成员,//std::string s=person1.Name; 不能访问类的私有成员,但是可以访问公有的成员,c++中默认的成员是私有的,与C#中一样,成员默认也是私有的 
     Person person2(2,"xiaohong",60,200000);//调用带有参数的构造函数
     person2.GetName();
     Person person3;//
     person3=person1;//给对象赋值,引用相同的成员函数地址,但是对象的成员数据值相等,存储地址不同
     person1=Person(2,"xiaoqiang",60,300000);//会调用带有参数的构造函数创建临时对象,赋值后,会立即调用析构函数删除该临时对象。
     person1.ComparePerson(person2);//const限定了返回的类型修饰,如果修改返回值是不允许的。参数通过引用传递
     person1.ComparePersonPassValue(person2);//const限定了返回的类型修饰,如果修改返回值是不允许的。参数通过值传递*/

     /*
     Person person4(2,"person4",60,200000);//调用带有参数的构造函数
     Person person5;
     person5=person4*1.5;  
     person5.GetName(); //无法通过person5.Money或者person5.Name直接输出,因为私有成员只能通过成员函数放问,而在C#中可以直接这样访问属性,体现了c#属性访问器的优势   
     person5=1.5*person4;//会调用友元函数重载的操作
     cout<<person5;//由于类重载了操作符<<,因此可以直接输出类类型的变量*/

     /*
     Person person6(6,"person6",80,20000000,"this is person6");
     cout<<person6;
     callperson1(person6);//参数按引用传递,效率较高
     cout<<person6;
     callperson2(person6);//按值传递参数,会导致产生临时的对象,会调用默认的复制构造函数和析构函数,由于成员中有指针变量,将释放指针变量指向的内容,导致person6中得成员指针指向的内容为乱码,
                             //所以要添加默认的复制构造函数,实现深度复制,不仅仅复制指针地址,而应该复制地址对应的内容
     cout<<person6;
     Person person7=person6;//给对象赋值,会调用复制构造函数。
     cout<<person7;
     Person person8;//调用默认的构造函数
     person8=person6;//会调用隐式的赋值操作符,实现成员的逐个复制。产生的问题还上面一样也是浅复制,析构后会对person6的指针成员变量有影响,所以要重载类的赋值操作运算符=。
     cout<<person8;*/

    /*
       Person* person9=new Person(9,"person9",80,9999,"this is person9");//new为对象动态分配内存空间,返回一个对象的指针,是给对象整体分配空间,会调用带有参数的构造函数
       cout<<*person9;                                                  //特别注意new分配的空间,不会自动调用析构函数释放存储空间,需要显示的使用delete

       Person person10(10,"person10",80,10000,"this is person10");//会调用带有参数的构造函数,自动调用析构函数释放空间
       cout<<person10;
       delete person9;//显示的调用析构函数,释放动态分配的空间,否则会出现内存泄露的现象*/

    /*
       Person person11(11,"person11",80,11111111,"this is person11");//生成一个基类对象
       cout<<person11;//调用基类的<<操作符输出基类
       ManOrWomenPerson person12(12,"person12",80,12121212,"this is person12",1);//生成一个派生类的对象,会先调用基类的构造函数,在调用派生类的构造函数
       cout<<person12;//调用基类的<<操作符输出基类
       person12.ShowSex();//调用派生类的显示性别的方法
       ManOrWomenPerson person13=person12;//会先调用基类的复制构造函数
       Person & rp=person12;    //在引用和指针的定义中,一般情况下引用和指针必须和它指向的对象属于同一类型,但是这点在类的继承中不一样
       Person * pp=&person12;   //基类的引用和指针可以指向派生类,但是访问引用和指针的时候,指向的又是派生类的对象
       cout<<rp;
       cout<<*pp;*/

    /*
     Person person13(11,"person13",80,13131313,"this is person13");//生成一个基类对象
     ManOrWomenPerson person14(14,"person14",80,12121212,"this is person14",1);//生成一个派生类的对象,会先调用基类的构造函数,在调用派生类的构造函数
     Person & rp=person13;    //在引用和指针的定义中,一般情况下引用和指针必须和它指向的对象属于同一类型,但是这点在类的继承中不一样
     Person * pp=&person14;   //基类的引用和指针可以指向派生类,但是访问引用和指针的时候,指向的又是派生类的对象
     rp.playway();//引用的类型是基类,对象也是基类,所以会调用基类的动作函数
     pp->playway();//指针的类型是基类,但是指向的对象是派生类,所以会执行派生类的动作函数
     
     Person* person15=new Person(15,"person15",80,15151515,"this is person15");//new为对象动态分配内存空间,返回一个对象的指针,是给对象整体分配空间,会调用带有参数的构造函数
     Person* person16=new ManOrWomenPerson(16,"person16",80,16161616,"this is person16",0);//new为对象动态分配内存空间,注意指针类型与对象的类型不一样
                                                                                           //如果基类的析构函数不是虚拟的话,将会只调用基类的析构函数
     delete person16;     //person16本来为派生类,我们认为应该先调用派生类的析构函数,在调用基类的析构函数,这样的顺序才是合理的,所以必须将基类的析构函数定义为虚拟的。
     delete person15;*/

  • 相关阅读:
    python 多进程队列数据处理
    python mqtt 客户端实现
    elasticsearch 父子关系
    elasticsearch Mapping使用自定义分词器
    elk 解决浏览器跨域问题
    elasticsearch 英文数字组合字符串模糊检索
    elasticsearch 关联单词查询以及Shingles
    elasticsearch 分析器 分词器
    Leetcode: Binary Tree Postorder Traversal
    Leetcode: Binary Tree Preorder Traversal
  • 原文地址:https://www.cnblogs.com/guoyuanwei/p/2130338.html
Copyright © 2011-2022 走看看