c++之类和对象的编程入门

c++ —类与对象入门(1)

类定义的语法形式:

class 类名称
{
 public:
    共有的(外部接口)
 private:
    私有成员
 protect:
    保护型成员
};
共有成员: 是类与外部的借口,任何外部函数都可以访问共有类型数据和函数。
私有成员: 仅允许本类中的函数访问,而类外的任何函数都不能访问。(如果私有成员在紧跟类名称的后面声明,则关键字private可以省略)
保护性成员: 与private类似,差别表现在继承与派生时对派生类的影响不同。(以后再细讲)

对象定义的语法形式:

要实现类中的功能,就要通过定义好的其对象来实现。

语法:
类名  对象名
例:
Clock myClock;

访问类内成员的方式:
对象名 . 类名
(仅访问public成员)

如何定义类的成员函数:

1、在类中要声明函数原型;
2、可以在类外给出函数体实现,并在函数名前使用类名加以限定;
3、也可以直接在类中给出函数体,形成内联成员函数;
3、允许声明重载函数和带默认参数值的函数
内联成员函数:
1、为提高运行效率,较简单的函数可以声明成内联形式;
2、内联函数体中不要有复杂结构(如循环语句和switch语句);
3、类中声明内联成员函数方式:将函数体放在类的声明中,并用inline关键字

类与对象实例:

在类外给出函数体实现:

#include <iostream>
using namespace std;
class Clock{
public:
	void setTime(int newH,int newM,int newS);//函数声明,此函数用来设置一个时间
	void showTime();//函数声明,此函数用来将设置的时间显示出来
private:
	int hour,minute,second;
};

void Clock::setTime(int newH,int newM,int newS)//类外给出设置时间函数体的实现
{
	hour=newH;
	minute=newM;
	second=newS;
}

void Clock::showTime()//类外给出显示时间函数体实现
{
	cout<<hour<<":"<<minute<<":"<<second<<endl;
}

int main()
{
	Clock myClock;
	myClock.setTime(11,11,11);//设置的时间为11:11:11
	myClock.showTime();//显示时间
	return 0;
}

用内联函数在类中给出函数体实现:

#include <iostream>
using namespace std;
class Clock{
public:
	inline void setTime(int newH,int newM,int newS)//内联函数inline
	{
	hour=newH;
	minute=newM;
	second=newS;
	}
	inline void showTime()//内联函数inline
	{
	cout<<hour<<":"<<minute<<":"<<second<<endl;
	}
private:
	int hour,minute,second;
};

int main()
{
	Clock myClock;
	myClock.setTime(11,11,11);
	myClock.showTime();
	return 0;
}

注:
1、其中inline也可以省略。
2、在类声明之后一定要在 “}” 后加上 “;” 。

构造函数:

构造函数作用: 类中的特殊函数,在对象创建时使用特定的值构造对象,将对象初始化为一个特定的初始状态。
构造函数形式:
1、函数名与类名相同;
2、不能定义返回值类型,也无return;
3、形式参数可有可无,有的话也可以带默认值;
4、可以为内联函数;
5、可以重载。
构造函数调用时机: 在创建对象时自动调用。

默认构造函数:

1、参数列表为空的构造函数;
2、全部参数都有默认值的构造函数。
例如以下两个构造函数如果在同一个类中出现,就会报错:
Clock ();
Clock (int newH=0;int newM=0;int newS=0);
因为在调用时二者均可以不给实参来调用,这时编译器就不知道该调用哪个了。
隐含生成的构造函数:
1、若未定义构造函数,编译器将自动生成一个默认构造函数;
2、其参数列表为空,不为数据成员设置初始值;
3、若类内定义了成员的初始值,则使用类内定义的初始值;
4、若类内未定义成员初始值,则以默认方式初始化;
5、基本类型的数据默认初始化的值时不确定的。
构造函数举例:

#include <iostream>
using namespace std;
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 Clock::setTime(int newH,int newM,int newS)
{
	hour=newH;
	minute=newM;
	second=newS;
}

void Clock::showTime()
{
	cout<<hour<<":"<<minute<<":"<<second<<endl;
}

int main()
{
	Clock myClock(0,0,0);
	myClock.showTime();       //显示的时间为0:0:0
	return 0;
}

默认构造函数举例:

#include <iostream>
using namespace std;
class Clock{
public:
	Clock(int newH,int newM,int newS);      //构造函数
	Clock();                               //默认构造函数
	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)
{}                                         //构造函数实体

Clock::Clock():
	hour(0),minute(0),second(0)
{}                                        //默认构造函数实体

void Clock::setTime(int newH,int newM,int newS)
{
	hour=newH;
	minute=newM;
	second=newS;
}

void Clock::showTime()
{
	cout<<hour<<":"<<minute<<":"<<second<<endl;
}

int main()
{
	Clock myClock1(11,11,11);   //此处调用的是构造函数
	Clock myClock2;            //此处调用的是默认构造函数
	myClock1.showTime();
	myClock2.showTime();
	return 0;
}

在写程序时,“int a;”这种定义后没有初始化的情况时常发生,就如例子中的“Clock myClock2;”一样,在定义完之后,才会用setTime函数来对这个对象赋值,就如定义完a后用a时才会“a=1”赋值。但若没有默认构造函数,“Clock myClock2;”的形式就会报错,所以为了避免这种情况发生,都会在类中定义一个默认构造函数来让程序更加通用。

委托构造函数:

构造函数中调用另一个构造函数。

#include <iostream>
using namespace std;
class Clock{
public:
	Clock(int newH,int newM,int newS);
	Clock();
	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)
{}

Clock::Clock():Clock(0,0,0)      //委托构造函数
{}

void Clock::setTime(int newH,int newM,int newS)
{
	hour=newH;
	minute=newM;
	second=newS;
}

void Clock::showTime()
{
	cout<<hour<<":"<<minute<<":"<<second<<endl;
}

int main()
{
	Clock myClock1(11,11,11);
	Clock myClock2;
	myClock1.showTime();
	myClock2.showTime();
	return 0;
}
复制构造函数

复制构造函数是一种特殊的构造函数,其形参为本类的对象的引用。作用是用一个已经存在的对象去初始化同类型的新对象。
class 类名{
public:
 类名(形参)          //构造函数
 类名(const 类名 &对象名)  //复制构造函数
… …
};
类名::类(const 类名 &对象名)    //复制构造函数的实现
{函数体}

复制构造函数被调用的三种情况:
1、定义一个对象时,用本类的另一个对象作为初始值;
2、如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造;
3、如果函数返回值类型是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造。(可通过移动构造避免不必要的复制,详见后续章节)
例:

#include <iostream>
using namespace std;
 
class Point{
public:
	Point(int xx,int yy):x(xx),y(yy){}  //构造函数
	Point():Point(0,0){}             //委托构造函数 
    Point(const Point &p);                //复制构造函数
    ~Point(){};                      //析构函数
	int getX(){return x;}
	int getY(){return y;}
private:
	int x,y;//私有成员
};
 
Point::Point(const Point &p)
{
	x = p.x;
	y = p.y;
	cout << "调用复制构造函数" <<endl;
}

//形参作为Point类对象的函数
void fun1(Point p)
{
	cout<< p.getX()<<endl;
 
}
 
//返回类的对象
Point fun2()
{
	Point a(1,2);
	return a;
}
 
int main()
{
	Point a;    //第一个对象a,调用默认构造函数(委托构造函数) 
	
	Point b(a);      //此时调用复制构造函数:用a初始化b,第一次调用复制构造函数
	cout << b.getX()<<endl; 
 
	fun1(b);  //此时调用复制构造函数:类的对象在函数中为实参,第二次调用复制构造函数
 
	b = fun2();//此时调用复制构造函数:函数返回值为类的对象,第三次调用复制构造函数
	cout << b.getX()<<endl; 
	return 0;
}

隐含的复制构造函数:
若未定义一个复制构造函数,编译器会自动生成一个隐含的复制构造函数。其功能是:用初始值对象的每个数据成员,初始化将要建立的对象的对应数据成员。

析构函数

在对象的生存期结束的时刻,系统会自动调用析构函数。
如果程序中未声明析构函数,编译器将自动产生一个默认的析构函数,其函数体为空。
析构函数原型: ~类名();
析构函数没有参数表,也没有返回值类型及return语句。

注:上面复制函数的例子中就有析构函数。

组合类

组合类的构造函数: 不仅要负责对本类的基本数据类型成员初始化,也要对对象成员初始化。
格式:
类名::类名(对象成员所需的形参,本类成员形参):
  对象1(参数),对象2(参数),… …
{
//函数体其他语句
}
构造组合类对象时的初始化次序:
1、首先对构造函数初始化列表中列出的成员(包括基本类型成员和对象成员)进行初始化,初始化次序是成员在类体中定义的次序:
成员构造函数调用的顺序:按对象成员的声明顺序,先声明者先构造。
初始化列表中未出现的成员对象调用默认构造函数(无形参的)初始化。
2、处理完初始化列表后,再执行构造函数的函数体
例:

#include <iostream>
#include <cmath>
using namespace std;

class Point{
public:
	Point(int a=0,int b=0);      //构造函数 
	Point(Point &p);             //复制构造函数 
	int getX(){return x;}
	int getY(){return y;}
private:
	int x,y;
};

Point::Point(int a,int b)         //构造函数实现 
{
	x=a;
	y=b;
	cout<<"using constructor of Point"<<endl;
}

Point::Point(Point &p)             //复制构造函数实现 
{
	x=p.x;
	y=p.y;
	cout<<"using copy constructor of Point"<<endl;
}	

//类的组合
class Line{
public:
	Line(Point xp1,Point xp2);
	Line(Line &l);
	double getLen(){return len;}
private:
	Point p1,p2;
	double len;
}; 

//组合类的构造函数
Line::Line(Point xp1,Point xp2) :     //形参实参结合执行两次Point的复制构造函数,从后往前执行 
	p1(xp1),p2(xp2)            //用形参将组合类初始化,执行两次Point的复制构造函数,按组合类Line定义的顺序:p1在前p2在后,则先初始化p1,再初始化p2 
{
	cout<<"using constructor of Line"<<endl;
	double x=(p1.getX()-p2.getX());
	double y=(p1.getY()-p2.getY());
	len=sqrt(x*x+y*y);
}

//组合类的复制函数 
Line::Line(Line &l):
	p1(l.p1),p2(l.p2)       //此处再调用两次Point的复制构造函数 
{
	cout<<"using copy constructor of Line"<<endl;
	len=l.len;
}

int main()
{
	Point myp1(1,1),myp2(4,5);      //执行两次Point的构造函数 
	Line line(myp1,myp2);           //执行四次Point的复制构造函数,一次Line组合类的构造函数
	Line line2(line);              //利用复制构造函数建立一个新对象,执行两次Point的复制构造函数,一次Line组合类的复制构造函数 
	cout<<"The length of the line is:";
	cout<<line.getLen()<<endl;
	cout<<"The length of the line2 is:";
	cout<<line2.getLen()<<endl;
	return 0;
}

结果:
在这里插入图片描述

前向引用声明

1、类应先声明后引用;
2、如果需要在某个类的声明之前引用该类(两个类互相引用),则应进行前向引用声明
3、前向引用声明只为程序引用一个标识符,具体声明在其他地方。
例:
class B;   //前向引用声明
class A{
 public:
  void f(B b);
};
class B{
 public:
  void g(A a);
};
注:
1、在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象;
2、只能使用被声明的符号,而不能涉及类的任何细节。
例:
class B;   
class A{
 B b;   //错误:类B的声明尚不完善
};
class B{
 A a;
};

结构体

c++中结构体是一种特殊形态的类。
与类的唯一区别:类的缺省访问权限是private;结构体的缺省访问权限是public。
什么时候用结构体而不用类:
1、定义主要用来保存数据,而没什么操作的类型;
2、人们习惯将结构体的数据成员设为公有,因此这时用结构体更方便。
结构体的定义:
struct 结构体名称{
  公有成员
protected:
  保护型成员
private:
  私有成员
};
与c结构体区别: c++中结构体成员既可以是数据成员,也可以是函数成员;c中的结构体成员只能为数据成员。
结构体的初始化:
如果:
1、一个结构体的所有数据成员都为公有成员;
2、没有用户定义的构造函数;
3、没有基类和虚函数(以后介绍)。
这个结构体的变量可以用下面的语法形式进行初始化:
类型名 变量名={数据成员1初始值,数据成员2初始值… …};
例:

#include <iostream>
#include <string>
using namespace std;

struct Student{
	string num;
	string name;
	char sex;
	int age;
};                             //结构体

int main()
{
	Student stu = {"201924100724","John",'M',19};
	cout<<stu.name<<endl;
	cout<<stu.num<<endl;
	cout<<stu.sex<<endl;
	cout<<stu.age<<endl;
}

联合体

与类和结构体差别:存储空间的共用。
定义形式:
union 联合体名称{
  公有成员
protected:
  保护型成员
private:
  私有成员
};
特点:
1、成员共用同一组内存单元;
2、任何两个成员不会同时有效。
例:
普通联合体;
union Mark{
 char grade;
 bool pass;
 int percent;
 //int为四个字节,占用的最大,则此联合体共占用四个字节
};
每使用一个数据成员,就会把其他的刷掉。
无名联合体
union{
 int i;
 float f;
};
在程序中可以这样使用:
i=20;
f=2.2;
i的值会被f刷掉,只存储了f的值。
例:

#include <iostream>
#include <string>
using namespace std;

class ExamInfo{
private:
	string name;
	enum {GRADE,PASS,PERCENTAGE} mode;    //定义一个枚举类型的mode
	union{
		char grade;
		bool pass;
		int percent;
	};
public:
	ExamInfo(string name,char grade):
		name(name),mode(GRADE),grade(grade){}
	ExamInfo(string name,bool pass):
		name(name),mode(PASS),pass(pass){}
	ExamInfo(string name,int percent):
		name(name),mode(PERCENTAGE),percent(percent){}
		void show();
};

void ExamInfo::show(){
	cout<<name<<":";
	switch(mode){
		case GRADE: cout<<grade;break;
		case PASS: cout<<(pass?"PASS":"FAIL");break;
		case PERCENTAGE: cout<<percent;break; 
	}
	cout<<endl;
}
int main()
{
	ExamInfo course1("English",'B');
	ExamInfo course2("Math",true);
	ExamInfo course3("C++ Programming",85);
	course1.show();
	course2.show();
	course3.show();
	return 0;
}

结果:
在这里插入图片描述

枚举类

注:与枚举类型不同!
语法形式:
enum class 枚举类型名:底层类型名{枚举值列表};
(枚举类型是枚举类中底层类型名为 int 的情况。)
枚举类优势:
1、强作用域,其作用域限制在枚举类中。
例:enum class Type:char{General,Light,Medium};
使用Type的枚举值General时,要Type::General
(避免不同的枚举类中枚举值重名问题)
2、转换限制,枚举类对象不可以与整型隐式地相互转换;
3、可以指定底层类型。

发布了30 篇原创文章 · 获赞 33 · 访问量 1277

猜你喜欢

转载自blog.csdn.net/weixin_45949075/article/details/104009639