保研笔记六 c++程序设计(面向对象)

目录

定义成员函数

内联成员函数

构造函数

析构函数

new和malloc、free和delete的区别和联系

静态成员

静态数据成员

静态成员函数

友元

拷贝构造函数

扫描二维码关注公众号,回复: 14491571 查看本文章

调用拷贝构造函数的三种情况

运算符重载

重载运算符

重载=运算符

拷贝构造函数和重载=函数的区别

重载++运算符

重载比较运算符

类的组合

构造函数析构函数调用顺序

继承、多态、虚函数

类的继承与派生

保护成员与类的访问

构造函数和析构函数

多态和虚函数

纯虚函数


默认情况下类的成员是私有的,而结构体的成员时共有的。

私有成员函数只能被本类的其它函数所调用. 

定义成员函数

成员函数在类之外定义的常规方式:    可以将参数传给私有成员变量。

<返回值类型>     <类名>::<函数名> ( 参数列表 )    {    }

void    Rectangle::setData(float  w, float  l )
{
	    width = w;
    	length = l;
}

内联成员函数

函数体出现在类的定义中,就叫做内联函数,一般是一些getset

构造函数

  • 与类同名。
  • 没有返回值类型,也不允许void类型
  • 允许为内联函数、重载函数、带缺省形参值的函数
class Clock
{
    public:
	    Clock (int NewH, int NewM, int NewS);//构造函数
	    void SetTime(int NewH, int NewM, int NewS);
	    void ShowTime();
    private:
	    int Hour,Minute,Second;
};
//构造函数实现
Clock::Clock(int NewH, int NewM, int NewS)
{
	Hour= NewH;
	Minute= NewM;
	Second= NewS;
}
//建立对象时构造函数的作用:
void main()
{
    Clock  c (0,0,0); //隐含调用构造函数,将初始值作为实参。
    c.ShowTime();
}

析构函数

  • 析构函数也与类同名,只是前面多了一个波浪号(~).
  • 当一个对象终止时会自动调用析构函数.
class PersonInfo  {
	char  *name;    	int     age;
public:
    	PersonInfo(char *n, int a) {
		name = new   char[strlen(n) + 1];
	 	strcpy(name, n);	    age = a; 
     	} 
     	~PersonInfo ( ) {  delete [ ] name;  }
     	char   *getName ( ) {  return   name;  }
     	int      getAge ( ) {   return   age;  }
}; 

new和malloc、free和delete的区别和联系

1、new 是c++中的操作符,malloc是c 中的一个函数

2、new 不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数

3、内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行,而malloc没有这些信息。

题目:

假定CMyClass是个类,则执行CMyClass a(4),b[3],*p[2]

调用该类的构造函数    4   次

a(4)1次+b[3]3次,没有初始化的指针为空,不指向任何对象,也不调用构造函数。

静态成员

静态数据成员

  • 用关键字static声明
  • 该类的所有对象维护该成员的同一个拷贝
  • 必须在类外定义和初始化,用(::)来指明所属的类。

静态成员函数

  • 类外代码可以使用类名和作用域操作符来调用静态成员函数。
  • 静态成员函数只能引用属于该类的静态数据成员或静态成员函数。静态成员函数不能访问非静态成员

友元

  • 友元函数不是类的成员,但可以访问类的私有成员变量。
  • 友元函数可能是一个独立的函数,或者是其它类的成员函数 ( 事实上一个完整的类都可以定义为另一个类的友元.)
//在Aux类中,访问Budget类中的成员
void   addBudget( float, Budget &);
//在Budget类中
friend  void  Aux::addBudget(float, Budget &);
//外部
void Aux::addBudget(float b, Budget &div)
{
	auxBudget = b;
	div.corpBudget += auxBudget;
}

可以把一个类定义成友元:friend   class   Aux

拷贝构造函数

拷贝构造函数是一个特殊的构造函数,当定义一个对象并采用同类型的另一个对象初始化时会自动调用。

PersonInfo   person1("Jones", 25);
PersonInfo   person2 = person1;  // ???

 PersonInfo ( const  PersonInfo  &obj)
 {
 	name = new char [strlen(obj .name) + 1];
	strcpy(name, obj.name);
	age = obj.age; 
 }

调用拷贝构造函数的三种情况

  • 当用类的一个对象去初始化该类的另一个对象时系统自动调用它实现拷贝赋值。
  • void main(void)
    {  Point A(1,2);
       Point B(A); //拷贝构造函数被调用
       cout<<B.GetX()<<endl;
    }
    
  • 若函数的形参类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数。
  • void fun1(Point p)
    {   cout<<p.GetX()<<endl;
    } 
    void main()
    {   Point A(1,2);
        fun1(A); //调用拷贝构造函数
    }     
    
  • 当函数的返回值是类对象时,系统自动调用拷贝构造函数。
  • Point fun2()
    {    Point A(1,2);
         return A; //调用拷贝构造函数
    }
    void main()
    {
         Point B;
         B=fun2(); 
    }
    

运算符重载

多态:多态性是指发出同样的消息被不同类型的对象接收时导致完全不同的行为。

多态的实现:-函数重载 -运算符重载 -虚函数

重载运算符

声明形式:函数类型  operator 运算符(形参){   ......}

重载为类成员函数时,参数个数=原操作数个数-1  (后置++、--除外)

重载为友元函数时,参数个数=原操作数个数,且至少应该有一个自定义类型的形参。

类成员:
complex operator + (complex c2); //+重载为成员函数
complex operator - (complex c2); //-重载为成员函数

complex complex::operator +(complex c2) //重载函数实现
{
	complex c(real+c2.real,imag+c2.imag);
	return c;
} 
//或:return complex(real+c2.real,imag+c2.imag)

友元:
friend complex operator + (complex c1,complex c2);	//运算符+重载为友元函数
friend complex operator - (complex c1,complex c2);	//运算符-重载为友元函数

complex operator + (complex c1,complex c2)	//运算符重载友元函数实现
{	  return      complex(c2.real+c1.real, c2.imag+c1.imag);
}
complex operator - (complex c1,complex c2)	//运算符重载友元函数实现
{	return       complex(c1.real-c2.real, c1.imag-c2.imag);
}

重载=运算符

#include <iostream.h>
#include <string.h>
class PersonInfo {
    	char *name;
    	int age;
public:
   	PersonInfo(char *n, int a) {
 		name = new char[strlen(n) + 1];
        strcpy(name, n);
        age = a; 
	}
   PersonInfo(const   PersonInfo   & obj ) {
	      name = new char[strlen(obj.name) + 1];
          strcpy(name, obj.name);      
          age = obj.age; 
   }
   ~PersonInfo( )   { delete [ ] name; }
   char  *getName( ){ return name; }
   int 	  getAge( ) { return age; }
   PersonInfo operator=(const  PersonInfo  &right){
 	    delete [ ] name;
    	name = new char[strlen(right.name)+ 1];
        strcpy(name, right.name);
	    age = right.age;
        return    *this;  
   }
};

this指针:一个隐含的内嵌指针,以隐含参数的形式传递给非静态的成员函数.

拷贝构造函数和重载=函数的区别

复制构造函数又称拷贝构造函数,它与赋值操作符间的区别体现在以下几个方面
1.从概念上区分:
复制构造函数是构造函数,而赋值操作符属于操作符重载范畴,它通常是类的成员函数
2.从原型上来区分:
复制构造函数原型ClassType(const ClassType &);无返回值
赋值操作符原型ClassType& operator=(const ClassType &);返回值为ClassType的引用,便于连续赋值操作
3.从使用的场合来区分:
复制构造函数用于产生对象,它用于以下几个地方:函数参数为类的值类型时、函数返回值为类类型时以及初始化语句,例如(示例了初始化语句,函数参数与函数返回值为类的值类型时较简单,这里没给出示例)

ClassType a;         
ClassType b(a);     //调用复制构造函数,初始化时拷贝
ClassType c = a;    //调用复制构造函数

而赋值操作符要求‘=’的左右对象均已存在,它的作用就是把‘=’右边的对象的值赋给左边的对象

ClassType e;
Class Type f;
f = e;              //调用赋值操作符,fe已存在

4.当类中含有指针成员时,两者的意义有很大区别
复制构造函数需为指针变量分配内存空间,并将实参的值拷贝到其中;而赋值操作符它实现的功能仅仅是将‘=’号右边的值拷贝至左值,在左边对象内存不足时,先释放然后再申请。当然赋值操作符必须检测是否是自身赋值,若是则直接返回当前对象的引用而不进行赋值操作

关于啥时候要用const:【C++】运算符重载关于const的分析(超详细)_萌宅鹿同学的博客-CSDN博客_c++ const 重载

重载++运算符

FeetInches   operator++(  );
FeetInches   operator++( int );
FeetInches   FeetInches::operator++(  )
{
    	++inches;
    	simplify ( );
    	return  *this;
}
FeetInches FeetInches::operator++(int)
{
	    FeetInches  temp(feet, inches);
    	inches++;
    	simplify( );
    	return  	temp;
 }

重载比较运算符

int operator>  (const   FeetInches & );
int operator<  (const   FeetInches & );
int operator== (const   FeetInches & );

int 	FeetInches::operator> (const    FeetInches  &right )
{   
    if (feet > right.feet)
     	  return   1;
    else  
        if (feet==right.feet && inches> right.inches)
    		 return   1;
        else     return    0;
}

int    FeetInches::operator<  (const FeetInches &right)
{   if (feet < right.feet)
    		return 1;
    else  
       if (feet == right.feet && inches < right.inches)
    		 return 1;
       else  	 return 0;
}

int     FeetInches::operator==(const FeetInches &right)
{
    if (feet == right.feet && inches == right.inches)
    		return 1;
    else 	return 0;
}

类的组合

类中的成员数据是另一个类的对象。

声明形式:

类名::类名(对象成员所需的形参,本类成员形参)        

:对象1(参数),对象2(参数),...... {  本类初始化  }

构造函数析构函数调用顺序

  • 先调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造)。
  • 然后调用本类的构造函数。(析构函数的调用顺序相反
  • 若调用缺省构造函数(即无形参的),则内嵌对象的初始化也将调用相应的缺省构造函数。

继承、多态、虚函数

  • 继承机制允许一个新的类建立在一个已经存在的类基础之上。
  • 新类将继承基类所有的成员变量和成员函数(除了构造函数和析构函数)

类的继承与派生

继承保持已有类的特性而构造新类的过程,被继承的已有类称为基类(或父类)

派生:在已有类的基础上新增自己的特性而产生新类的过程,派生出的新类称为派生类。

继承的目的:实现代码重用。

派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。

声明方式:class 派生类名:继承方式  基类名 {         成员声明; }

class Grade {
	char    letter;
	float    score;
	void     calcGrade(  );
public:
	void  setScore(float s) { score = s;  calcGrade(); }
	float getScore( ) {  return score; }
	char getLetter( ) { return letter; }
};

class  Test : public   Grade {
	int	  numQuestions;  //问题个数
	float	  pointsEach;  //每个问题分数
	int	  numMissed; //错误问题数
public:
	Test( int,  int );
};


// q = 问题个数, m = 错误问题数.
//********************************************
Test::Test(int q, int m)
{	float     numericGrade;

	numQuestions = q;
	numMissed = m;
	pointsEach = 100.0 / numQuestions;
	numericGrade = 100.0 - ( numMissed * pointsEach );
    //派生类可以调用基类成员,基类不能调用派生类成员
	setScore(numericGrade);
}

// 主函数里
// 定义一个Test对象
	Test    exam(questions, missed);
	cout.precision(2);
	cout << "\n The score is " << exam.getScore();
	cout << "\n The grade is " << exam.getLetter();

保护成员与类的访问

基类的保护成员和私有成员类似,但是可以被派生类所访问。

private 

基类私有成员对于派生类来说是不可访问的.

基类保护成员在派生类中变成私有成员

基类公有成员在派生类中成为私有成员

protected 

基类私有成员对于派生类来说是不可访问的.

基类保护成员在派生类中成为保护成员

基类公有成员在派生类中成为保护成员

public

基类私有成员对于派生类来说是不可访问的.

基类保护成员在派生类中成为保护成员

基类公有成员在派生类中成为公有成员

 

构造函数和析构函数

调用顺序:基类的构造函数先于派生类的构造函数执行。 析构函数调用顺序相反。

传递参数:派生类的构造函数需传递参数给基类的构造函数。

Cube::Cube(float w, float l, float h) : Rect(w, l )
{    	height = h ;    volume = area * height ;
}

多态和虚函数

  • 虚函数是动态联编的基础。 是非静态的成员函数。
  • 在类的声明中,在函数原型之前写virtual。
  • virtual 只用来说明类声明中的原型,不能用在函数实现时。
  • 具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。
  • 本质:不是重载声明而是同名覆盖。
  • 调用方式:通过基类指针或引用,执行时会 根据指针指向的对象的类,决定调用哪个函数。
#include <iostream.h>
class B0	//基类B0声明
{
public:	//外部接口
	virtual void display()
     {cout<<"B0::display()"<<endl;}
                                         //虚成员函数
};
class B1: public B0	//公有派生
{ public:
	  void display()
     {  cout<<"B1::display()"<<endl;  }
};
class D1: public B1	//公有派生
{ public:
	  void display()
     {  cout<<"D1::display()"<<endl;  }
};
void fun(B0 *ptr)	//普通函数
{    ptr->display();    }
void main()	//主函数
{	B0 b0,  *p;	//声明基类对象和指针
	B1 b1;	//声明派生类对象
	D1 d1;	//声明派生类对象
	p=&b0;
	fun(p);	//调用基类B0函数成员
	p=&b1;
	fun(p);	//调用派生类B1函数成员
	p=&d1;
	fun(p);	//调用派生类D1函数成员
}


程序的运行结果为:

B0::display()
B1::display()
D1::display()

纯虚函数

纯虚函数是在基类中声明的虚函数,声明时无函数体,要求继承它的子类必须覆盖该虚函数,带有纯虚函数的类成为抽象类。 抽象类不能实例化,但其派生类可以。

一个纯虚函数:  virtual void showInfo(void) = 0;  

#include <iostream.h>
class B0      //抽象基类B0声明
{
    public:        //外部接口
	virtual void display( )=0;
                                   //纯虚函数成员
};
class B1: public B0	//公有派生
{
    public:
	void display(){cout<<"B1::display()"<<endl;}	
                                                               //虚成员函数
};
class D1: public B1	//公有派生
{
    public:
	void display(){cout<<"D1::display()"<<endl;}
                                                               //虚成员函数
};
void fun(B0 *ptr)	//普通函数
{	ptr->display();   }

void main()	//主函数
{	B0 *p;	//声明抽象基类指针
	B1 b1;	//声明派生类对象
	D1 d1;	//声明派生类对象
	p=&b1;
	fun(p);	//调用派生类B1函数成员
	p=&d1;
	fun(p);	//调用派生类D1函数成员
}

程序的运行结果为:

B1::display()
D1::display()

猜你喜欢

转载自blog.csdn.net/LarsGyonX/article/details/125698226