类的实现

在C++中,引出了类的定义,C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
1.在C++中,在结构体内部,不仅可以定义变量,还可以定义函数,只不过在C++中,更喜欢用class来代替strcuct。 那么就可以来按照结构体的框架来定义一个类了
struct默认的成员都是public的(因为struct要兼容C),class默认的成员都是private的
class StuInfor
{
public:
	类方法
	void Show()
	{}
	void GetName()
	{}
	void GetSex()
	{}
	void GetAge()
	{}
	类成员
	char name[10];
	char sex[3];
	int age;
};//注意后面要加上分号

注意:类方法可以在类里面进行实现,也可以只在类里声明,在类外实现。只不过在类外实现时要加作用域访问符。

class StuInfor
{
public:
	void Show();
	void GetName();
	void GetSex();
	void GetAge();
	类成员
	char name[10];
	char sex[3];
	int age;
};
//在类外实现方法,要加上类名和作用域访问符::,这样才能说明是这个类的方法,而非其它类的方法
void StuInfor::Show()
{}
void StuInfor::GetName()
{}
void StuInfor::GetSex()
{}
void StuInfor::GetAge()
{}

类里面有三种修饰限定符,它们各自的作用域是从当前限定符到下一个限定符之间的所有成员

public:在类外,可以直接访问public修饰的成员
private:修饰的成员无法在类外被直接访问
protect:修饰的成员无法在类外被直接访问

所以,在类外,我们可以直接通过调用类里被public所修饰的方法来间接操控数据。

class StuInfor
{
public:
	void GetInfor(char*name,char*sex,int age);
private:
	char _name[10];
	char _sex[3];
	int _age;
};
void StuInfor::GetInfor(char*name,char*sex,int age)
{
	strcpy(_name,name);
	strcpy(_sex,sex);
	_age = age;
}
void main()
{
	StuInfor stu;
	stu.GetInfor("张三","nan",20);//通过public方法来操纵数据.
}
2.一个类的大小
class StuInfor
{
public:
	类方法
	void Show()
	{}
private:
	类成员
	char name[10];
	char sex[3];
	int age;
};

void main
{
	StuInfor stu;
	cout<<sizeof(stu)<<endl;//输出为8.
}
3.隐藏的this指针

C++编译器给每个成员函数增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。这些都是编译器自动完成的。

class Data
{
public:
	SetData(int year,int month,int day)//函数的参数应该是SetData(Data *const this,int year,int month,int day)
	{
		this->m_year = year;
		this->m_month = month;
		this->m_day = day;
		                   	//this可以省略,所以一般都不写,但是要知道这个是有指针指向的。
	}
private:
	int m_year;
	int m_month;
	int m_day;
};
void main()
{
	Data d;
	d.SetData(1999,6,3);//实际上函数的参数应该是SetData(&d,1999,6,3);
}

注:

1.this指针的类型是什么?
答:类型为 类名 * const ,加const的原因是因为防止函数内部把this指针改变
2.this指针可以为空吗?
答:可以,如果函数内部没有用到this指针,比如只打印一条语句,是可以为空的,但是在函数内部,如果有用到this指针,或者有this指针所指向的内容,这时候this指针就不能为空。
3.this指针存在于哪?
答:this指针参数则是存放在寄存器中。

4.类的基本函数
①:构造函数:对对象进行初始化(不是创造对象)

1.函数名和类相同
2.无返回值
3.可以重载
4.定义对象时,如果传参了,调用有参数的构造函数,没传参调用没参数的构造函数,如果不写,系统默认调用自己的构造函数

class test
{
public:
	test(int x,int y,int z):a(x),b(y),c(z)
	{}
private:
	int a ;
	int b;
	int c;
};
void main()
{
	test t1(1,2,3);
}

注:除了上面赋值可以对对象进行初始化,还可以采用初始化列表的方式来进行初始化,在构造函数后加:,然后每个成员后面跟一个括号,括号里面放初始化的值或表达式,成员之间用逗号隔开

class test
{
public:
	test(int x, int y,int z) :a(x), b(y), c(z)
	{}
private:
	int a;
	int b;
	int c;
};
void main()
{
	test t1(1, 2, 3);
}

有几种成员只能通过初始化列表进行初始化
1.由const修饰的成员
2.由引用修饰的成员
3.初始化的成员是对象的时候,即成员是由另一个类来定义的时候只能采用初始化

class Date
{
public:
	Date(int z) :_year(z)
	{}
private:
	int _year;
};
class test
{
public:
	test(int x,int y,int z) :a(x), b(y), d(z)
	{}
private:
	int &a;
	const int b;
	Date d; ///这三个必须通过初始化列表来初始化
};
void main()
{
	test t(100,200,300);
}

注:其次在构造函数前加explicit,可以防止隐式转换

class Test
{
public:
	Test(int data = 0) :m_data(data)
	{}
private:
	int m_data;
};
int main()
{
	Test t;
	t = 10;若是光看这一行代码,会认为t是int类型,实际上t是Test类型,这就是隐式转换。为了避免这种歧义,可以加上explicit防止其隐式转换。
	return 0;
}
class Test
{
public:
	explicit Test(int data = 0) :m_data(data)
	{}
private:
	int m_data;
};
int main()
{
	Test t;
	t = 10;这样就编译不通过了,要想编译通过可以强转。
	t = (Test)10;
	return 0;
}
class Date
{
public:
	Date(int year) :_year(year)//可以编译通过
	{}
	explicit Date(int year) :_year(year)//不能编译通过
	{}
	Date()
	{

	}
	Date&operator=(const Date&d) 
	{
		if (this != &d)
		{
			_year = d._year;
		}
		return *this;
	}
private:
	int _year;
};
void main()
{
	Date d(10);
	d = 2019;//,看起来这句代码是int类型给Date类型赋值,本质上代码是用2019初始化一个对象,然后对象给对象赋值,进行操作符重载,这就是隐式转换,加上explicit可以避免这种隐式转换
}
②:析构函数:对对象做最后的处理(不是销毁)

1.函数名为类名前加取反符号~
2.无参无返回值
3.不能重载
4.由于是在栈上创建的对象,所以先定义的对象后调用析构函数,后定义的对象先调用构造函数

class test
{
public:
	~test()//析构函数
	{
		strcpy(name,"zhangsan");
		strcpy(sex, "nan");
		age = 15;
	}
private:
	char name[20];
	char sex[5];
	int age;
};
void main()
{
	test t1;//后调用析构函数
	test t2;//先调用析构函数
}
③:拷贝构造函数(构造函数的重载)

1.拷贝构造函数的参数只能有一个且必须使用引用传参,使用传值方式会引发无穷递归调用
2.拿对象初始化对象时会调用拷贝构造函数,包括传值,拿对象做返回值

class test
{
public:
	test(const test&t)//拷贝构造函数   test(test*const this,const test&t)
	{
		strcpy(name,t.name);
		strcpy(sex,t.sex);
		age = t.age;
	}
private:
	char name[20];
	char sex[5];
	int age;
};
void main()
{
	test t1;
	test t2 = t1;
	test t3(t1);
}

注:

1.拷贝构造函数的参数可以不可以不要const?
答:可以,不过为了防止在拷贝构造函数内部将参数的值修改,还是加上const
2.拷贝构造函数的参数可不可以传值?
答:不可以,传值会引发无限递归,传值会一直初始化对象,就会一直调用拷贝构造函数,所以不行,只能使用传引用。

④.赋值运算符重载

1.函数名为operator后面接需要重载的运算符符号,其余和函数相同,可以有参数,可以有返回值
2. . .* sizeof ?: :: 这5个运算符不能重载
3.赋值运算符的四点注意:
a. 参数类型(传引用,加上const)
b. 返回值(引用返回)
c. 检测是否自己给自己赋值(if语句的判断)
d. 返回*this(return *this)

class test
{
public:
	test&operator=(const test&t)//运算符重载  test&operator(test*const this,const test&t)
	{
		if(this!=&t)//防止自我赋值
		{
			strcpy(name,t.name);
			strcpy(sex,t.sex);
			age = t.age;
		}
		return *this;
	}
private:
	char name[20];
	char sex[5];
	int age;
};
void main()
{
	test t1;
	test t2;
	t1 = t2;//   相当于t1.operator=(t2);operator=是函数名,t2是参数,t1的地址本来也是函数的参数,只不过这里没写。
}

注:

1.运算重载的函数参数可以不可以不要const?
答:可以不要,但为了防止在函数内部把参数的值改掉,所以还是加上const.
2.运算重载的函数参数可以不可以使用参数传值和用值返回?
答:可以,但是在参数传值和用值返回时,是要初始化对象的,所以会调用一次拷贝构造函数,而传引用是不会调用的。所以为了效率,最好还是用引用传参和引用返回。
3.运算重载可不可以不返回?
答:可以,但是不能连等了,例如上面的代码,如果不要返回值,就不能写成 t1 = t2 = t3 …这样的,但是t1 = t2还是可以的。因为连等的操作本质上这样的 t1.operator=(t2.operator=(t3)) ,如果没有返回值,这样那么t2.operateor这个函数就没参数,这样就无法正常调用。

⑤:const修饰的成员函数

1.在函数后加const是const修饰的成员函数,在函数后加const实际上修饰的是this指针,表明在该函数内部不能对this指针指向的内容进行修改
2.方法和常方法可以共存

class Data
{
public:
	void show()//void show(Data*const this)
	{
		cout<<"year = "<<year<<endl;
	}
	void show()const//void show(const Data*const this)
	{
		cout<<"const year = "<<year<<endl;
	}
private:
	int m_year
};
void Test ()
{
 Date d1 ;
 d1.show(); //调void show()
 const Date d2;
 d2.show();//调void show()const
}

注意:
1.非const对象可以调用非const成员函数和const成员函数
2.const对象可以调用const成员函数,不能调用非const成员函数
3.非const成员函数可以调用其他非const成员函数和其他const成员函数
4.const成员函数可以调用其他const成员函数,不能调用其他非const成员函数

⑥:取地址即const取地址操作符重载

这两个一般不用写,系统会自动生成。

class Data
{
public:
	Data* operator&()
	{
		return *this;
	}
	const Data* operator&()const
	{
		return this;
	}
private:
	int m_year;
	int m_month;
	int m_day;
};
5.静态(static)成员

由static修饰的成员是静态成员,由static修饰的成员函数是静态成员函数
注:1.静态成员的初始化必须在类外进行,并且初始化时不加static
2.静态成员函数没有this指针,不能访问非静态成员函数
3.类的静态成员函数不属于任何对象,所以不能通过对象来调用函数,但可以通过类名来调用类的静态函数

class Date
{
public:
	static void fun()
	{
		cout<<"static fun "<<endl;
	}
private:
	static int _year;
};
int Date::_year = 10;//static成员函数初始化必须在类外进行
void main()
{
	Date t(10);
	Date::fun();//通过类名可以调用类的静态函数
}

注:1.非静态成员函数可以调用静态成员函数吗?反过来呢?
非静态成员函数可以调用静态成员函数,静态成员函数不能调用非静态成员函数

例:

class Date
{
public:
	static void show1()
	{
		show2();//static修饰的show1不能调用show2()
	}
	void show2()
	{
		show1();//show2()可以调用static修饰的show1()
	}
private:
	static int _year;
};
int Date::_year = 10;
void main()
{
	Date t;
	t.show1();
	t.show2();
}
6.友元
①:友元函数

友元函数是由friend来修饰的函数,在类里声明时,不受限制词的约束,在类外实现时,不加friend。
注:1.友元函数可以直接访问类的私有成员
2.友元函数不拿const修饰
3.一个函数可以是多个类的友元函数

class Date
{
	friend ostream& operator<<(ostream& out, const Date&d);//标准输出流的友元函数
	friend istream& operator>>(istream&in, const Date&d);//标准输入流的友元函数
public:
private:
	static int _year;
};
int Date::_year = 10;
ostream& operator<<(ostream& out, const Date&d)
{
	out << d._year;
	return out;
}
istream& operator>>(istream&in, const Date&d)
{
	in >> d._year;
	return in;
}
void main()
{
	Date t;
	cin >> t;
	cout << t << endl;
}
②:友元类

由static修饰的类为友元类,当一个类被修饰为友元时,它的所有成员函数都可能是另一个类的友元函数,都可以访问另一个类的非公有成员
即A是B的友元类,则A就可以访问B中的非公有成员
注:友元是单向的,不具有传递性。,即A是B的友元类,B是C的友元类,但A不是C的友元类。

class Date
{
	friend class Time;//声明Time类是Date类的友元类,所以在Time里面可以访问Date的私有成员
public:

private:
	static int a;
	int b;
};
class Time
{
public:
	void show()
	{
		cout << d.a << endl;
	}
private:
	static int _year;
	int _month;
	Date d;
};
int Time::_year = 10;
int Date::a = 10;
void main()
{
	Time tm;
	tm.show();
}
7.内部类

一个类定义在另一个类的内部,所以这个类就叫内部类,此时这个外部类对内部类没有任何的访问权限。
注:1.内部类就是外部类的友元类,而外部类不是内部类的友元类
2.sizeof(类) = sizeof(外部类),和内部类没有关系
3.内部类访问外部由static修饰的成员,枚举成员,可以直接访问,不需要通过外部对象来调,访问外部普通成员时,必须通过外部成员来调

此时Time类就是Date类的友元类
class Date
{
public:
	class Time
	{
	public:
		void show();
		void fun(const Date&d)
		{
			a = 10;
			cout << d.d << endl;可以通过外部成参数来访问外部的私有成员
		}
	private:
		static int b;
		int c;
	};
private:
	static int a;
	int d;
	
};
int Date::a = 10;
int Date::Time::b = 20;//通过外部类和内部类的限制来初始化b
void Date::Time::show()//通过外部类和内部类的限制来实现方法
{
	cout << Date::a << endl;
	cout << Date::Time::b << endl;
}
void main()
{
	Date d;//定义一个Date类
	Date::Time t;//定义一个Time类,t可以访问d的成员,d不能访问t的成员,因为t是d的友元类
	t.show();
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值