c++学习笔记一

我们两清 提交于 2020-03-22 14:54:28

定一个头文件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;*/

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!