C++类的介绍与使用 Class

类的定义与目的:

定义:一种数据类型
目的:归纳某种对象,包含该对象的各种属性

类的特性:

1.类中的参数默认是私有的,只能提供类中的函数进行访问

class 与 struct 的区别

  1. class中成员默认为private,struct中成员默认为public
  2. struct留下是为了兼容C语言

类的使用:

1.类的创建:

class Player{
public://变为公有,才能访问
	int hp,mp;
};// 不要漏了 ";"

int main(){
	Player player;
	player.hp=1;
	player.mp=1;
}

2.构造函数:

函数名为类名的成员函数,不可能为静态函数。

1、无参构造函数:

若没有调用构造函数,会自动调用该函数。C++自己有默认的构造函数

class Temple{
	int x,y;
	Temple(){
		x=1;
		y=1;
	}
}

2、一般构造函数:

带有参数的构造函数,可以有多个构造函数。会根据传入的参数来调用不同的构造函数。

class Temple{
	int x,y;
	Temple(){
		x=1;
		y=1;
	}
	Temple(int x){
		this.x=x;
		y=-1;
	}
	Temple(int a,int b):x(a),y(b){
		
	}
	Temple(Temple temple){
		x=temple.x;
		y=temple.y;
	}
}
int main(){
	Temple t1;//无参
	Temple t2(2);// 第二个
	Temple t3(2,2)// 第三个
	Temple t4(t1)//第四个
}

注意:要使用class或者struct 作为STL容器的类型时,若自定义有参数的构造函数,需要明写出无参的构造函数。因为作为容器类型时,会调用无参的构造函数,若没有明写出,会出错

成员变量列表

class Example{
	int x,y;
	Example(int X,int Y):x(X),y(Y){}
	Example(int a,int b):y(a),x(b){}//error x=a,y=b,按照声明的顺序进行初始化,不理前面的名称
}

与在函数体内构造的区别:
1.当成员变量中有类对象(自己写的、string)时,在函数体内会生成两个对象,第一个对象使用默认构造函数生成,第二个再根据情况生成并覆盖原对象,原对象丢弃。

class Example{
	Example(){
		cout<<"No1"<<endl;
	}
	Example(int x){
		cout<<"No2"<<endl;
	}
};
class Ex{
	string str;
	Example e;
	Ex(){
		str="YOU";//
		e=new Example(1);//会输出 No1 与No2
	}
}
str会先调用默认构造函数生成对象,再使用"YOU"来生成另一个对象并覆盖原本的,原本的被丢弃
e会调用默认构造函数生成对象,再使用参数构造函数生成另一个并覆盖原本的,原本的被丢弃

而使用列表则只会根据情况生成一个对象,减少消耗

class Ex{
	string str;
	Example e;
	Ex():str("YOU"),e(Example(1)){};// 只输出 No2
};

3.拷贝构造函数

拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构造及初始化。利用旧对象构造新对象。
唯一的形参必须是对象的引用,一般会加上const。

由于同一个类的对象在内存中有完全相同的结构,因此作为一个整体进行复制或称拷贝是完全可行的。而且拷贝过程中只需要拷贝成员变量,因为函数成员是共用的(只有一份拷贝)。
在类中如果没有显式给出拷贝构造函数时,则C++编译器自动给出一个缺省的拷贝构造函数

使用拷贝构造函数的情况
1)类对象作为函数的参数,以值传达的方式传入。

class Ex{
};
void Funtion(Ex ex){}// 作为函数的参数

2)作为函数的返回值,以值传达的方式返回。从局部对象到临时对象的拷贝构造。

class Ex{
};
Ex Funtion(Ex ex){
	return ex;
}

3)用一个对象给另一个对象进行初始化,常称为赋值初始化。

int main(){
	Ex e1(1,2);
	Ex e2(e1);
	Ex e3=e1;
}

默认拷贝构造函数
没有定义拷贝构造函数的情况下,C++会生成默认的拷贝构造函数来实现对象的拷贝。等效于下面的拷贝构造函数。

class Ex{
	int a,b;
	int* c;
	Ex(const Ex& ex){
		a=ex.a;
		b=ex.b;
		c=ex.c;// 指针也是直接等于
	}
};

但默认拷贝构造函数有个缺点,它是浅拷贝。若成员变量中包含指针,则新旧对象中该指针会指向同一个内存地址。之后无论新旧对象哪个修改了的指针的值,两个对象的都会发生改变。
因此包含指针时,应进行深拷贝。让两个指针指向不同的内存地址。


class Ex{
	int a,b;
	int* c;
	Ex(const Ex& ex){
		a=ex.a;
		b=ex.b;
		c=new int;// 对于指针,不是简单的等于。而为其申请地址
		*c=*(ex.c);
	}
};

4.子类使用父类的构造方法

3.析构函数:

当类对象被删除,即超出作用域时,会调用析构函数,可以在该函数释放成员变量所占内存。

class Temple{
	int *x;
	Temple(){
		x=new int [10];// 分配内存
		cout<<"Creat"<<endl;
	}
	~Temple(){// 析构函数
		free(x)// 释放x所占的内存
		cout<<"Over"<<endl;
	}
}

4.类中的方法:

可以设置为private或public
class Player{
public://变为公有,才能访问
	int hp,mp;
	void Change(int h,int m){
		hp=h;
		mp=p;
	}
	void Change1(int hp,int mp){// 与类中成员同名时,使用this代表类中的成员
		this.hp=hp;
		this.mp=mp;
	}
};// 不要漏了 ";"

类的继承:

1.使用方法:

拥有父类的所有成员,大小也包含父类的大小
注意:基类的指针可以指向子类,但子类的指针不能指向基类。

class Base{
	int x,y;
	void Move(){
		return x;
	}
};
class Player: public Base{// 继承了 Base类
	char* name;
	void Print(){
			cout<<name<<endl;
	}
};
int main(){
	Player player;
	player.x=5;
	Player* p=new Base();//合法
	Base* base=new Player();// 非法
	
}

2.虚函数 virtual:

用于实现多态,子类可以重写父类的虚函数。
原理:利用了动态联编,通过虚函数表来在运行时确定调用的函数。
对于非虚函数,在编译时就确定要调用哪个函数,因此调用非函数

虚函数表:

当一个类包含虚函数时,会生成虚函数表,保存虚函数的地址。其派生类也会有虚函数表
创建类实例时,若有虚函数,则会生成虚函数指针,指向其虚函数表。
当一个基类的指针指向了 子类对象,则运行时,会使用子类的虚函数表指针寻找子类虚函数的地址,而不是基类的。

编译器创建虚函数表的过程:
1.拷贝每个基类的虚函数表。
2.查看子类是否重写了基类的虚函数,有则修改子类的虚函数表;查看子类是否增加了虚函数,有则加入虚函数表

使用情况:

有一个基类与一个子类

class Base(){
	void Print1(){// 非虚函数
		cout<<"Base"<<endl;
	}
	virtual void Print2(){
		cout<<"Base"<<endl;
	}
};
class Son():public Base{
	void Print1(){
		cout<<"Son"<<endl;
	}	
	void Print2() override{// 重写虚函数,加上override关键字,非必要,但可以检测错误
		cout<<"Son"<<endl;
	}
};

1.基类指针指向子类,且调用非虚函数:会调用基类的函数,而不是子类的函数

int main(){
	Son* s=new Son();
	Base* base=s;// 基类指针指向
	base->Print1();// 输出“Base”,非虚函数
}

2.基类指针指向子类,且调用虚函数:会根据虚函数表调用子类函数,

int main(){
	Son* s=new Son();
	Base* base=s;// 基类指针指向
	base->Print2();// 输出“Son”,虚函数
}

虚函数的缺点:

  1. 增大内存的消耗
  2. 每次调用虚函数时,要遍历虚函数表

3.纯虚函数(接口)

基类的虚函数只是一个声明,并没有实现。子类必须实现该虚函数。
使用场景:可以用于制作模板,规定子类都必须实现某些函数
有纯虚函数的子类,不能实例化。

class Base(){
	virtual void Print()=0;// 纯虚函数

};
class Son1():public Base{
	void Print() override{// 实现纯虚函数,加上override关键字,非必要,但可以检测错误
		cout<<"Son1"<<endl;
	}
};
class Son2():public Son2{
	void Print() override{// 由于基类实现了纯虚函数,所以该类可以不重写Print()
		cout<<"Son2"<<endl;
	}
};
void PrintName(Base* b){
	b->Print();
}
int main(){
	Base* base=new Base();// 会报错,不能实例化
	Son1* son1=new Son1();
	Son2* son2=new Son2();
	PrintName(son1);// 输出“Son1”
	PrintName(son2);// 输出“Son2”
}

可见性

1.private

只能在该类的内部访问,子类也不行。只能提供类中的函数进行调用与修改,在其他地方不能使用

class Entity{
private:
	int x,y;
	int PrintX(){
		return x;
	}
public:
	int Print(){
		return PrintX();// 只能在类在的函数进行调用
	}
};
class Son:public Entity{
public:
	Son(){
		x=2;// error,子类也不行
		PrintX();// error
	}
}
int main(){
	Entity e;
	e.x=2;// error
	e.PrintX();// error
	e.Print();
}

2.protected

自己与子类可以访问

class Entity{
protected:
	int x,y;
	int PrintX(){
		return x;
	}
public:
	int Print(){
		return PrintX();// 只能在类在的函数进行调用
	}
};
class Son:public Entity{
public:
	Son(){
		x=2;// OK,子类可以
		PrintX();// OK
	}
}
int main(){
	Entity e;
	e.x=2;// error
	e.PrintX();// error
	e.Print();
}

3.public

任何地方都可以访问。

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值