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;*/