C++类与对象

一、类的定义

C++类定义的格式:

class 类名

{

public:

          公有数据成员和成员函数;

protected:

          保护数据成员和成员函数;

private:

          私有数据成员和成员函数;

}分号不能省略

各成员函数的实现;

(1)关键词private用于声明私有成员。私有成员只能在类内可见,不能在类外或者派生类中使用。

         如果私有成员放在第一段,则可省略关键词private

(2)protected声明保护成员。保护成员在类和它的派生类中可见。

(3)public声明共有成员。共有成员是类的接口,在类中和类外可见。

class Student//定义学生类
{
    public://声明类成员
    void Getinfo(string pname,string pid,char sex,int a,double s);
    void modify(float s);
    void display();
    private:
    string name;
    string id;
    char sex;
    int age;
    double score;
};类定义以分号结束
注意事项

(1)类的成员可以是其他类的对象,但不以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。

(2)类与结构体的区别:没有明确指定类成员的访问权限时,C++结构体的成员是共有的,而类的成员是私有的。

二、成员函数

以前所学的函数不是任何类的成员函数,可称为“全局函数”。

成员函数的实现可以位于类的定义之外,格式如下:

返回值类型 类名::函数名(参数表)

{

   函数体

}

作用域分符由两个冒号构成,它用于标识属于什么类的成员

Date类的成员函数定义在类外写为:
void Date::SetDate(int y,int m,int d)
{
    year=y;
    month=m;
    day=d;
}
int Date::IsLeapYear()
{
    return(year%4==0&&year%100!=0)||(year%400==0);
}
void Date::PrintDate()
{
    cout<<year<<"."<<month<<"."<<day;
}
简单的成员函数实现可在类中定义,此时,编译器作为内联函数处理。
例如成员函数SetDate在类中写成:
//……
public:
void SetDate(int y,int m,int d)
{
    year=y;
    month=m;
    day=d;
}

三、对象

对象是类的实例或者实体。

类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。

1.对象的定义

格式:类名 对象名1,对象名2,……,对象名n;

注意:必须在定义了类之后,才可以定义类的对象。
class Point	
{
public:	 
	void InitPoint(float PointA_x, float PointA_y); 
	void Move(float New_x, float New_y);
	float GetPointx();
	float GetPointy();
private:
	   float P1_x,P1_y;
};
int main()
{
      Point  p1,p2;
}

2.类成员的访问

(1)圆点访问形式:对象名.共有成员

CRectangle r1,r2;
CRectangle &rr=r2;
rr.w=5;
rr.init(5,4);//rr的值改变,r2的值也改变

(2)指针访问形式:对象指针变量名—>共有成员

CRectangle r1,r2;
CRectangle* p1=&r1;
CRectangle* p2=&r2;
p1—>w=5;//w属于p1指向的对象
p2—>init(5,4);//init函数作用在p2指向的对象上

注意:调用成员函数时,必须指明其所作用的对象。对于上面的CRectangle类来说,如果只写“init(5,4);”这条语句是不能编译通过的。因为编译器是不知道这个init函数是作用在那个对象上面的。

3.例题

/*写一个程序,输入矩形的长和宽,输出面积和周长
将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一个“矩形类”。
长、宽变量成为该“矩形类”的“成员变量”,三个函数成为该类的“成员函数” */
class CRectangle
{
	public:
		int w,h;
		int Area() 
		{  
			return w * h;      
		}
		int Perimeter()	
		{ 
			return 2 * ( w + h);  
		}
		void Init( int w_,int h_ ) {
			w = w_;  h = h_;
		}
};
int main( ) 
{
	 int w,h;
	 CRectangle r;  
	 cin >> w >> h;
	 r.Init( w,h);
	 cout << r.Area() << endl << r. Perimeter();
	 return 0;
}

4.类定义和使用时应注意: 

(1)在类的定义中不能对数据成员进行初始化。
(2)类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
(3)类中的数据成员可以是C++语法规定的任意数据类型。
(4)类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
(5)类定义必须以分号“;”结束
(6)class与struct的不同:①class中,成员缺省情况是private。②struct中,成员缺省情况是public。

四、内联函数

内联函数作用:

减少频繁调用小子程序的运行的时间开销

内联函数机制:

编译器在编译时,将内联函数的调用以相应代码代替

内联函数声明:inline 函数原型

注:内联函数仅在函数原型作一次声明。

       适用于只有1 ~5行的小函数

       不能含有复杂结构控制语句 ,不能递归调用

成员函数的类内实现(内联函数)

  class Coord{
    public:
        void setCoord(int a,int b)
        { x=a; y=b;}
        int getx()
        { return x;}
        int gety()
        { retrun y;}
   private:
        int x,y;
   };

五、成员函数可以重载

(1)函数重载:函数名相同,但参数不相同(类型不同,或者个数不同)的一组函数。

(2)编译器根据不同参数的类型和个数产生调用匹配

(3)函数重载用于处理不同数据类型的类似任务 

重载示例:

(1)参数个数相同,参数类型不同

#include<bits/stdc++.h>
using namespace std ;
int  abs ( int  a ) ;
double  abs ( double  f ) ;
int main ()
  {  cout << abs ( -5 )  << endl ;
      cout << abs ( -7.8 ) << endl ;
  }
int  abs ( int  a )
  {  return  a <  0  ?  -a  :  a ;  }
double  abs ( double  f )
  {  return  f < 0  ?  -f  :  f ;  }

(2)参数个数不同

#include<bits/stdc++.h>
using namespace std ;
int max(int a,int b) ;
int max(int a,int b,int c) ;
int main ()
 { cout<<max(5,3)<<endl ;
   cout<<max(4,8,2)<<endl ;
 }
int  max(int a,int b)
 { return a>b?a:b; }
int  max(int a,int b,int c )
 { int t;
    t=max(a,b);
    return max(t,c);
 }
#include<bits/stdc++.h>
using namespace std;
int max(int a,int b)
{
    cout<<"max 1"<<endl;
}
double max(double a,double b)
{
    cout<<"max 2"<<endl;
}
double max(double a,double b,double c)
{
    cout<<"max 3"<<endl;
}
int main()
{
    max(3,4);//调用int max(int,int)
    max(2.4,6.0);//调用double max(double,double)
    max(1.2,3.4,5);//调用double max(double,double,double)
    max(1,2,3);//调用double max(double,double,double)
    max(3,1.5);//编译出错
}

结果:
max 1
max 2
max 3
max 3

注意:同名函数只有参数表不同才能算重载。两个同函数名的参数表相同而返回值类型不同不是重载,而是重复定义,是不允许的。

六、构造函数

(1)构造函数是用于创建对象的特殊成员函数

        当创建对象时,系统自动调用构造函数

(2)构造函数的作用是:  为对象分配空间;对数据成员赋初值;请求其他资源

(3) 没有 用户定义的构造函数时,系统提供 缺省 版本的构造函数

(4) 构造函数名与类名相同:类名

(5)构造函数可以 重载

(6)构造函数可以有任意类型的参数,但 没有返回类型

1.默认构造函数(无参构造函数)

 如果类中没有定义构造函数,系统将自动生成一个默认形式的构造函数,用于创建对象,默认构造函数形式: 

 类名::类名(){} 

默认构造函数是一个空函数

为类Date建立一个构造函数。
#include <iostream.h>
class Date {
    public:
	    Date();     // 无参构造函数
	    Date(int y,int m,int d); 
	    void showDate();
    private:
            int year, month, day;
    };
Date::Date()    // 构造函数的实现
{  year=0; month=0; day=0; }
Date::Date(int y,int m,int d) 
{ year=y; month=m; day=d; }
inline void Date::showDate()
{  cout<<year<<"."<<month<<"."<<day<<endl; } 
int main()
{
     Date a_date,b_Date(2014,3,25);   
     a_date.showDate();
     b_date.showDate();
     return 0;
}

2.

class Complex
{
    private:
    double real,imag;
    public:
    Complex(double r,double i=0);//第二个参数的默认值为0
};
Complex::Complex(double r,double i)
{
    real=r;imag=i;
}
以下语句
Complex c1;//错误,Complex类没有无参构造函数
Complex* pc=new Complex;//错误,Complex类没有无参构造函数
Complex c2(2);//正确,相当于Complex c2(2,0)
Complex c3(2,4),c4(3,5);//正确
Complex* pc2=new Complex(3,4);//正确

3.

构造函数是可以重载的,即可以写多个构造函数,他们的参数表不同。当翻译到能生成对象的语句时,编译器会根据这条语句所提供的参数信息决定该调用哪个构造函数。如果没有提供参数信息,编译器就认为应该调用无参构造函数。

class Complex
{
    private:
    double real,imag;
    public:
    Complex(double r);
    Complex(double r,double i);
    Complex(Complex c1,Complex c2);
};
Complex::Complex(double r)//构造函数1
{
    real=r;imag=0;
}
Complex::Complex(double r,double i)//构造函数2
{
    real=r;imag=i;
}
Complex::Complex(Complex c1,Complex c2)//构造函数3
{
    real=c1.real+c2.real;
    imag=c1.imag+c2.imag;
}
int main()
{
    Complex c1(3),c2(1,2),c3(c1,c2),c4=7;
    return 0;
}
c1,c2,c3,c4分别用构造函数1,2,3,4进行初始化
初始化结果是c1.real=3,c1.imag=0(也写作c1={3,0})
            c2={1,2}
            c3={4,2}
            c4={7,0}

4.构造函数在数组中的使用

#include<bits/stdc++.h>
using namespace std;
class CSample
{
    public:
    CSample(){
    cout<<"constructor 1 called"<<endl;
    }//构造函数1
    CSample(int n){
    cout<<"constructor 2 called"<<endl;
    }//构造函数2
};
int main()
{
    CSample array1[2];
    cout<<"step1"<<endl;//array数组中的两个元素没有指明如何初始化,使用无参构造函数初始化
    CSample arrat2[2]={4,5};
    cout<<"step2"<<endl;
    CSample array3[2]={3};
    cout<<"step3"<<endl;
    CSample* array4=new CSample[2];
    delete [] array4;
    return 0;
}

输出结果
constructor 1 called
constructor 1 called
step1
constructor 2 called
constructor 2 called
step2
constructor 2 called
constructor 1 called
step3
constructor 1 called
constructor 1 called

在构造函数有多个参数时,数组的初始化列表中要显式包含对构造函数的调用。例如:

class CTest{
  public:
   CTest(int n){}//构造函数1
   CTest(int n,int m){}//构造函数2
   CTest(){}//构造函数3
   };
int main()
{
    CTest array1[3]={1,CTest(1,2)};//三个元素分别用构造函数1,2,3初始化
    CTest array2[3]={CTest(2,3),CTest(1,2),1};//2,2,1
    CTest* pArray[3]={new CTest(4),new CTest(1,2)};//两个元素指向的对象分别用构造函数1,2初始化
    return 0;
}
/*pArray数组是一个指针数组,其元素不是CTest的对象,而是CTest的指针。
CTest* pArray[3]={new CTest(4),new CTest(1,2)}对pArray[0]和pArray[1]进行了初始化把他们初始化为指向动态分配的CTest对象的指针,而这两个动态分配的CTest对象又分别是用构造函数1,2初始化的,pArray[2]没有初始化,其值是随机的,不知道指向哪里。所以只生成了两个CTest对象,而不是三个,只调用了两次CTest类的构造函数*/

5.利用构造函数创建对象有两种方法

(1)利用构造函数直接创建对象,其一般形式为:

类名 对象名【(实参表)】;

这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数。 

int main()
{
	    Date *date1;
            date1=new Date(1998,4,28);     
		//  以上两条语句可合写成:Date *date1=new Date(1998,4,28);
	    cout<<"Date1 output1:"<<endl;
	    date1->showDate();          
	    delete date1;
             return 0;
}

(2)利用构造函数创建对象时,通过指针和new来实现。其一般语法形式为:

      类名 *指针变量= new 类名[(实参表)];

 例如:Date *date1=new Date(1998,4,28);就创建了对象(*date1)。 

6.构造函数的初始化列表—数据成员的初始化

构造函数初始化成员有两种方法

A.使用构造函数的函数体进行初始化

class Date
{
	int d,m,y;
public:
	Date(int dd, int mm, int yy)
	{
		d=dd;
		m=mm;
		y=yy;
	}
	Date(int dd, int mm)
	{
		d=dd;
		m=mm;
	}
}
B.使用构造函数的初始化列表进行初始化 

格式:

funname(参数列表):初始化列表

{  函数体,可以是空函数体  }


初始化列表的形式:

成员名1(形参名1),成员名2(形参名2),成员名n(形参名n) 
class Date
{
  int d,m,y;
public:
  Date(int dd, int mm, int yy):d(dd),m(mm),y(yy){}
  Date(int dd, int mm): d(dd),m(mm){}
}

必须使用参数初始化列表对数据成员进行初始化的几种情况

1.数据成员为常量

2.数据成员为引用类型

#include <iostream>
using namespace std;
class A{
public:
	A(int i):x(i),rx(x),pi(3.14)
	{}
	void display()
	{cout<<"x="<<x<<"rx="<<rx<<"pi="<<pi<<endl;}
private:
	int x,&rx;
	const float pi;
};
int main(){
	A aa(10);
	aa.display();
	return 0;
}

3.数据成员为没有无参构造函数的类的对象

#include<iostream>
using namespace std ;
class A
{ public :
    A ( int x ) : a ( x ) { }
    int a ;
} ;
class B
{ public :
    B( int x, int y ) : aa( x ),  b( y ) { }
    void out()
      { cout <<" aa = " << aa.a << endl << "b = " << b << endl ; }
  private :
      int b ;
      A aa ;
} ;
int main ()
  { B objB (3,5);
     objB.out () ;
  } 

类成员的初始化的顺序:

按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关

#include <iostream>
using namespace std;
class CMyClass{
public:
	CMyClass(int x, int y):m_y(x),m_x(m_y)
	{
		cout<<"m_x="<<m_x<<endl;
		cout<<"m_y="<<m_y<<endl;
	}
private:
	int m_x,m_y;
};
int main()
{
	CMyClass mc(15,10);
	return 0;
}

运行结果:

m_x=-858993460

m_y=15

构造函数的重载

class Box{
public:
	Box();
	Box(int,int,int);
	int volume();
private:
	int height,width,length;
};
Box::Box()
{	
height=10;	
width=10;	
length=10;
}
Box::Box(int h, int w,int l):height(h),width(w),length(l){}
int Box::volume()
{	return width*length*height;    }
int main(){
	Box box1;
	cout<<"The volume is "<<box1.volume();
	Box box2(12,30,25);
	cout<<"The volume is "<<box2.volume();
	return 0;
}

带默认值的构造函数

#include <iostream>
using namespace std;
class Box
{
public:
        Box();  //定义了全部带默认值的构造函数,不能再定义无参构造函数
	Box(int h=10,int w=10 , int l=10); //只能在声明时指定默认值
	int volume();
private:
	int height,width, length;
};
Box::Box(int h, int w,int l):height(h),width(w),length(l){}
int Box::volume(){
   return width*length*height;
}
int main()
{
	Box box1;
	 cout<<"The volume is "<<box1.volume();
	Box box2(12,30,25);
	 cout<<"The volume is "<<box2.volume();
        Box box3(30,25);
	 cout<<"The volume is "<<box3.volume();
	return 0;
}

注:构造函数一般被定义为公有成员

七、析构函数

析构函数是成员函数的一种,他的名字与类名相同,但前面要加“~”,没有参数和返回值。

对象生存期结束时,需要做清理工作,比如:释放成员(指针)所占有的存储空间。

析构函数可以完成上述工作。

定义格式如下(类外实现): 

类名::~类名()

{

       函数语句

}

  析构函数有以下一些特点:

(1) 析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);

(2) 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;

(3) 当撤消对象时,编译系统会自动地调用析构函数。 

(4)一个类有且仅有一个析构函数

(5)如果定义类时没写析构函数,则编译器生成默认析构函数。如果定义了析构函数,则编译器不生成析构函数。

可以定义析构函数在对象消亡前做善后工作。例如,对象如果在生存期间用new运算符动态分配了内存,则在各处写delete语句以确保程序的每条执行路径都能释放这片内存是比较麻烦的事情。有了析构函数,只要在析构函数中调用delete语句,就能确保对象运行中用new运算符分配的空间消亡时被释放。

class String
{
    private:
      char* p;
    public:
      String(int n);
      ~String ();
};
String::~String()
{
    delete []p;
}
String::String(int n)
{
    p=new char[n];
}
//String类的成员指向动态分配的一片存储空间,用于存放字符串,动态内存分配在构造函数中进行,而空间的释放在构造函数~String()中进行。这样,在其他地方就不用考虑释放空间的事情了。
#include<bits/stdc++.h>
using namespace std;
class CDemo
{
    public:
    ~CDemo()
    {
        cout<<"Destructor called"<<endl;
    }
};
int main()
{
    CDemo array[2];//构造函数调用2次
    CDemo* pTest=new CDemo;//构造函数调用
    delete pTest;
    cout<<"————————————"<<endl;
    pTest=new CDemo[2];//构造函数调用2次
    delete []pTest;
    cout<<"Main ends."<<endl;
    return 0;
}
输出
Destructor called
————————
Destructor called
Destructor called
Main ends.
Destructor called
Destructor called

默认析构函数 

若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。

系统自动生成的默认构造函数形式如下: 

类名::~类名(){}

一般情况下,可以不定义析构函数

但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收

#include <bits/stdc++.h>
using namespace std;
class Student{
public:
	Student(int n, string a_name, char s)	
{
	num=n;	
	name=a_name;
	sex=s;	
        cout<<"Constructor called."<<endl;
	
}
~Student()
   {	
    cout<<"Destructor called."<<endl;	}
  void display()
   {
     cout<<name<<endl;cout<<num<<endl;cout<<sex<<endl;	
   }
private:
  int num;  	
  string name;	
  char sex;
};
int main()
{
	Student stud1(10001,"Wang_li",'f');
	stud1.display();
	Student stud2(10002,"Zhao_Wu",'s');
	stud2.display();
	return 0;
}

输出

Constructor called.

Wang_li

10001

f

Constructor called.

Zhao_Wu

10002

s

Destructor called.

Destructor called.

Press any key to continue

#include <bits/stdc++.h>
using namespace std;
class Student{
public:
      Student(int n, char * a_name, char s)	
  {
	num=n; name=new char[strlen(a_name)+1];
	strcpy(name,a_name);sex=s;
	cout<<"Constructor called."<<endl;
   }
~Student()
   {
	cout<<"Destructor called."<<endl;
	delete[] name;
   }
void display()
   {
        cout<<name<<endl;cout<<num<<endl;cout<<sex<<endl;	
   }
private:
	int num; 	
        char *name; 	
        char sex;
};
class String
{
    private:
      char* p;
    public:
      String(int n);
      ~String ();
};
String::~String()
{
    delete []p;
}
String::String(int n)
{
    p=new char[n];
}
//String类的成员指向动态分配的一片存储空间,用于存放字符串,动态内存分配在构造函数中进行,而空间的释放在构造函数~String()中进行。这样,在其他地方就不用考虑释放空间的事情了。

class String
{
    private:
      char* p;
    public:
      String(int n);
      ~String ();
};
String::~String()
{
    delete []p;
}
String::String(int n)
{
    p=new char[n];
}
//String类的成员指向动态分配的一片存储空间,用于存放字符串,动态内存分配在构造函数中进行,而空间的释放在构造函数~String()中进行。这样,在其他地方就不用考虑释放空间的事情了。

class String
{
    private:
      char* p;
    public:
      String(int n);
      ~String ();
};
String::~String()
{
    delete []p;
}
String::String(int n)
{
    p=new char[n];
}
//String类的成员指向动态分配的一片存储空间,用于存放字符串,动态内存分配在构造函数中进行,而空间的释放在构造函数~String()中进行。这样,在其他地方就不用考虑释放空间的事情了。

class String
{
    private:
      char* p;
    public:
      String(int n);
      ~String ();
};
String::~String()
{
    delete []p;
}
String::String(int n)
{
    p=new char[n];
}
//String类的成员指向动态分配的一片存储空间,用于存放字符串,动态内存分配在构造函数中进行,而空间的释放在构造函数~String()中进行。这样,在其他地方就不用考虑释放空间的事情了。

int main()
{
	Student stud1(10001,"Wang_li",'f');
	stud1.display();
	Student stud2(10002,"Zhao_Wu",'s');
	stud2.display();
	return 0;
}

八、this指针

this指针指向了成员函数作用的对象,在成员函数执行的过程中,正是通过this指针才能够找到对象所在的地址,因而也就能找到对象的所有非静态成员变量的地址

#include<bits/stdc++.h>
using namespace std;
class A
{
    int i;
    public:
    void Hello(){cout<<"hello"<<endl;}
};
int main()
{
    A* p=NULL;
    p->Hello();
}
//p->Hello();实质上应该是Hello(p)。虽然p是一个空指针,但是在翻译后的Hello函数中,cout语句没有用到this指针,因此依然可以输出正确结果。如果Hello函数中有对成员变量的访问,则程序就会出错。

#include <iostream>
using namespace std;
class Time
{
public:
	int hour;
	int minute;
	int sec;
	void setHour( ){hour=10;}
};
int main( )
{ 	cout<<sizeof(Time)<<endl;	return 0; }
类的成员函数如何区分不同的实例对象的数据成员呢?
C++为此专门设立了一个名为this的指针,用来指向不同的对象。
一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。

需要显式引用this指针的三种情况

1)在类的非静态成员函数中返回类对象本身对象的引用的时候,直接使用 return*this,返回本对象的地址时return this

2)当参数与成员变量名相同时,如this->x= x,不能写成x = x

3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。

函数返回对象的引用

#include <iostream>
#include <string>
using namespace std;
class Person{
public:
	Person(string n, int a)	{
		name = n; //这里的 name 等价于this->name
		age = a; //这里的 age 等价于this->age
	}
	int get_age(void) const{ return age;}
	Person& add_age(int i)	{age += i; return *this;  }
private:
	string name;
	int age;
};
int main()
{
	Person Li("Li", 20);
	cout<<"Li age = "<< Li.get_age()<<endl;
	cout<<"Li add age = "<< Li.add_age(1).get_age()<<endl;  
		 //增加1岁的同时,可以对新的年龄直接输出;
	return 0;
}

结果:

  Li age = 20

  Liadd age = 21

参数与成员变量名相同。
#include <iostream>
using namespace std;
class Point
{
public:
	int x;
	Point ():x(0){}
	Point (int a){  x=a;  }
	void print(){   cout<<"x = "<<x<<endl;  }
	void set_x(int x){  x = x;  }
};
int main(){
	Point pt(5);
	pt.set_x(10);
	pt.print();
	return 0;
}
程序执行结果为:
x = 5
若将set_x函数改为:
	void set_x(int x) {this->x = x;}
程序执行结果为:
	x = 10
避免对 同一对象 进行赋值操作

#include <iostream>
using namespace std;
class Location 
{
     int X,Y;  //默认为私有的
 public:
     void init(int x,int y) { X =x; Y = y;};
     void assign(Location& pointer);
     int GetX(){ return X; }
     int GetY(){ return Y; }
};
void Location::assign(Location& pointer)
{
    if(&pointer!=this) //同一对象之间的赋值没有意义,所以要保证pointer不等于this
    {  X=pointer.X;  Y=pointer.Y; }
}
 int main(){
        Location x;
        x.init(5,4);
        Location y;
    	y.assign(x);
    	cout<<"x.X = "<< x.GetX()<<" x.Y = "<<x.GetY();
    	cout<<"y.X = "<< y.GetX()<<" y.Y = "<<y.GetY();
    	return 0;
}
九、复制构造函数

类名:: 类名const  类名  &  引用名  ,  …);

(const 保护实参对象只读

1. 复制构造函数名与类名相同,并且也没有返回值类型。
2. 复制构造函数可写在类中,也可以写在类外。
3. 复制构造函数 要求有一个类类型的引用参数。
4. 如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。 

#include<bits/stdc++.h>
using namespace std;
class Complex
{
    public:
    double real,imag;
    Complex(double r,double i)
    {
        real=r;
        imag=i;
    }
    Complex(const Complex&c)
    {
        real=c.real;
        imag=c.imag;
        cout<<"Copy Constructor called"<<endl;
    }
};
int main()
{
    Complex c1(1,2);
    Complex c2(c1);//调用复制构造函数
    cout<<c2.real<<","<<c2.imag;
    return 0;
}

输出
Copy Constructor called
1,2
自己编写的复制构造函数不一定要做复制的工作,可以real=2*c.real;imag=c.imag+1;


复制构造函数被调用的三种情况

(1)声明语句中用类的一个已知对象初始化该类的另一个对象时。 

Complex c2(c1);

或者Complex c2=c1;

注意:第二条语句是初始化语句,不是赋值语句。赋值语句的左边是一个早有定义的变量,赋值语句不会引发复制构造函数的调用。

例如

Complex c1,c2;

c1=c2;(该语句不会引发复制构造函数的调用,因为c1早已生成,已经初始化过了

(2)当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。 

(3)对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。

十、友元函数

如果在本类 A 以外的其他地方定义了一个函数 函数 B )。 这个函数可以是不属于任何类的非成员函数, 也可以是其他类的成员函数, 在类体中用 friend 对其 函数 B 进行声明,此函数就称为本类 A 的友元函数。
友元函数 函数 B 可以访问这个类 A 中的私有成员。

友元函数不是这个类的成员函数,可以是一个普通的全局函数,这个类把这个全局函数声明为友元以后,这个有元函数就可以访问这个类的私有成员了(但是 破坏了系统的封装性 ,一般不用)

定义一个boat类和Car类,均含有weight数据成员。
定义一个友元函数, 该函数能输出一个boat对象和一个car对象的重量和
#include <iostream>
using namespace std;
class Car;//声明
class Boat{
	int weight;
public:
	Boat(int w):weight(w){}
	friend void display_weight(Boat &boat ,Car &car);
};
class Car{
	int weight;
public:
	Car(int w):weight(w){}
	friend void display_weight(Boat &boat ,Car &car);
};
void display_weight(Boat &boat, Car &car){  cout<<boat.weight+car.weight<<endl;}
int main(){
    Boat boat(100);
     Car car(200);
    display_weight(boat,car);
    return 0;
}

用友元函数计算两点之间的距离

#include<bits/stdc++.h>
using namespace std ;
class Point
{ public:
      Point(double xi, double yi) { X = xi ; Y = yi ;}
      double GetX() { return X ; }
      double GetY() { return Y ; }
      friend double Distance ( Point & a, Point & b ) ;
  private:  double X, Y ;
} ;
double Distance(Point & a, Point & b )	
  {  double dx = a.X - b.X ;
     double dy = a.Y - b.Y ;
     return sqrt ( dx * dx + dy * dy ) ;
  }
int main()
{ Point  p1( 3.0, 5.0 ) , p2( 4.0, 6.0 ) ;
  double  d = Distance ( p1, p2 ) ;
  cout << "This distance is " << d << endl ;
} 

学习心得

C++和C语言解决问题的思想不一样,C++是面向对象的,首先要考虑我们要解决一个什么样的问题,需要哪些操作去实现,编程是次要的。

C语言之间的组织比较松散,C++是以不同的类构成的,每个类用来解决不同的问题,各类分工明确,所以C++可以支持复杂的开发模式,但是对于简单程序来说,可能会比C语言复杂一些。

使用C++编写程序需要考虑很多问题,可以先从最基本的开始,然后慢慢分支,逐步完善其他功能。

C++调试程序时要注意一个一个类来调试,一个调通了之后再调下一个,切忌全打出来以后再调,用“滚雪球”的方式。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值