c++之类与对象

类与对象

上篇

1.类的定义:
class className{
//类体
};
class 为定义类的关键字,className为类的名字。注意类结束之后的分号。
类中的元素称为类的成员,类中的数据成为类的属性或者成员变量,类中的函数称为类的方法。
2.类的两种定义方式
(1)声明和定义全部放在类中(这样编译器可能会将成员函数当成内联函数对待);
(2)声明放在.h文件中,定义放在类的实现文件.cpp中。
3.类的访问限定符以及封装
(1)访问限定符:
public,private,protected。
注意:public修饰的成员在类外可以直接被访问;
class默认private,struct默认public
(2)封装
面向对象三大特性:继承,封装,多态
封装的本质是一种管理:开放想让你访问的成员函数或者变量。
4.类的作用域
(1)类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外定义成员,需要使用::作用域解析符指定成员属于那一个类域。

clss person{
public:
	void printperson();
private:
	char _name[20];
	char _gender[3];
	int _age;
};
void person::printperson()
{
	//这里就是在类外,因此需要指明类域。
}

5.类的实例化

clss person{
public:
	void printperson();
private:
	char _name[20];
	char _gender[3];
	int _age;
};
int main(){
person A;//A就是person类的实例化。
return 0;
}

6.类对象的存储方式
只保存成员变量,成员函数存在在公共代码段(节省空间)。
例如:

#include<iostream>
using namespace std;
class  A
{
public:
	void f1(){}

private:
	int _a;

};

class A2
{
public:
	void f2(){}
};

//A3是空类
class A3
{
};

int main()
{
	printf("A1=%d\n", sizeof(A));
	printf("A1=%d\n", sizeof(A2));
	printf("A1=%d\n", sizeof(A3));

	return 0;
}

![因为只计算自己的成员变量,由于A2和A3都无成员变量,应该为0;但是编译器为了表示占位,表示成员变量存在过,因此是1](https://img-blog.csdnimg.cn/20210601234320756.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25ubHVvcA==,size_16,color_FFFFFF,t_70)7.结构体内存对齐规则1)第一个成员在与结构体偏移量为0;
(2)其他成员需要对齐到某个数字的整数倍的地址处;
(3)对齐数=编译器默认的一个对齐数 与该成员大小的较小值。
(4)结构体的总大小为:最大对齐数的整数倍。
8.this指针
c++编译器给每个“非静态的成员的函数”增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该对象的对象),在函数体中所有成员变量的操作都是通过该指针去访问的。
(2this指针的特性
类型为: 类* const
只能在成员函数的内部使用
this指针的本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。
this指针是成员函数第一个隐含的指针形参。
例如:

```cpp
//display是date类的成员函数
void display()//void display(Date * this)
{
}

中篇

1.构造函数
主要完成初始化工作,构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初值,并且在对象的声明周期内只调用一次。
特征如下:
(1)函数名与类名相同
(2)无返回值
(3)对象实例化时编译器自动调用对应的构造函数
(4)构造函数可以重载

#include<iostream>
using namespace std;
class Date {
public:
//构造函数
Date(int year = 0, int month = 1, int day = 1)
{
		_year = year;
		_month = month;
		_day = day;
}
private:
	int _year;
	int _month;
	int _day;
};

默认构造函数有三种:1.不写,编译器会自动生成;2.无参构造函数;3.全缺省构造函数。(默认的意思就是不需要传参。)
对于c++编译器而言,内置类型不需要初始化,自定义类型需要自己写初始化函数。
2.析构函数
对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
析构函数是特殊的成员函数。
其特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值。
  3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
typedef int DataType;
class SeqList
{ 
public :
 SeqList (int capacity = 10)//构造函数
 {
 _pData = (DataType*)malloc(capacity * sizeof(DataType));
 assert(_pData);
 _size = 0;
 _capacity = capacity;
 }
 ~SeqList()//析构函数
 {
 if (_pData)
 {
 free(_pData ); // 释放堆上的空间
 _pData = NULL; // 将指针置为空
 _capacity = 0;
 _size = 0;
 }
 }
 
private :
 int* _pData ;
 size_t _size;
 size_t _capacity;
};

3.拷贝构造函数
是构造函数的一个重载形式,参数只有一个且必须使用引用传参,使用传值会引发无穷递归调用。

lass Date
{
public:
//构造函数
Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 //拷贝构造,是构造函数的重载形式。
 Date(const Date& d)
 {
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
 Date d2(d1);//调用拷贝构造,隐藏有this指针。拿一个已经存在的对象去构造初始化创建另一个对象
 return 0;
}

注意:(1)传参的时候使用传值的话,层层传值引发对象的拷贝的递归调用。因为传值又是一种拷贝构造,一直传值一直拷贝构造,停不下来。
(2)编译器默认生成的拷贝构造完成的是浅拷贝,因此对于自定义类型,需要调用他的拷贝构造成员进行拷贝。

4.赋值运算符重载问题
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的;操作符有一个默认的形参this,限定为第一个形参。
.* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。

// bool operator==(Date* this, const Date& d2)
 // 这里需要注意的是,左操作数是this指向的调用函数的对象
 bool operator==(const Date& d2)
 {
 return _year == d2._year;
 && _month == d2._month
 && _day == d2._day;
 }

这里就是一个==的重载。

Date& operator=(const Date& d)
 {
 if(this != &d)
 {
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }
 }

这就是赋值运算符的重载。第一个参数为this指针,进行隐藏了。
这个函数在调用的时候先会调用拷贝构造。返回引用。

5.const成员
将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。(保护this指针)
在这里插入图片描述

  1. const对象不可以调用非const成员函数
  2. 非const对象可以调用const成员函数
  3. const成员函数内不可以调用其它的非const成员函数
  4. 非const成员函数内可以调用其它的const成员函数
    const修饰的成员变量,权限很小;在访问的时候遵循一个规则:权限只能缩小,不能放大。

下篇

1.再谈构造函数
初始化分为两种:一种是函数体内初始化;一种为初始化列表。

#include<iostream>
using namespace std;
class Date {
public:
//构造函数
Date(int year = 0, int month = 1, int day = 1)
{
		_year = year;
		_month = month;
		_day = day;
}
private:
	int _year;
	int _month;
	int _day;
};

这种就是函数体内初始化;

//初始化列表
//初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

#include<iostream>
using namespace std;
class Date {
public:
//构造函数 --这就是初始化列表
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}
private:
	int _year;
	int _month;
	int _day;
};

注意:引用成员变量,const成员变量,自定义类型成员(该类没有默认构造函数)必须在初始化列表的位置进行初始化。
且成员变量在类中声明次序就是在其初始化列表中的初始化顺序。

class A
{
public:
  A(int a)
    :_a1(a)
    ,_a2(_a1)
  {}
  void Print()
  {
    cout<<_a1<<" "<<_a2<<endl;
  }
private:
  int _a2;
  int _a1;
}
int main()
{
  A aa(1);
  aa.Print();
}

此题运行结果为 1,随机值。就是考察的初始化顺序问题。
2.explicit关键字
作用:用此关键字修饰构造函数,将会禁止但参构造函数的隐式转换。

Date(int year)
 :_year(year)
 {}
 
 explicit Date(int year)
 :_year(year)
 {}

禁止了隐式转换。

3.static成员
static修饰成员–静态 成员;static修饰成员函数–静态成员函数
可以理解为静态成员只要突破类域就可以访问。
注:静态成员没有隐含的this指针,因此不能访问任何静态成员。

class A
{
public:
 A() {++_scount;}
 A(const A& t) {++_scount;}
 static int GetACount() { return _scount;}
private:
 static int _scount;//声明
 };
int A::_scount = 0;//静态变量的定义
void TestA()
{
 cout<<A::GetACount()<<endl;
 A a1, a2;
 A a3(a1);
 cout<<A::GetACount()<<endl;
}

在此代码中,构造的匿名对象,生命周期只在这一行,声明周期结束后,调用析构函数。
因此定义一个对象要用,但是只用一行,一般采用匿名对象。
注:1.静态成员变量必须在类外定义,定义时不添加static关键字。
2.静态成员为所有类对象所共享,不属于某个具体的实例;
3.类的静态成员通过::,突破类域来访问。

4.友元函数和友元类
友元函数:可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加上friend关键字。
例如A是B的友元类,A可以访问B(私有成员),但是B不可以访问A里面的,因为是友元关系是单向的。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声
明,声明时需要加friend关键字。

class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);//声明<<是Date类的友元
friend istream& operator>>(istream& _cin, Date& d);
public:
 Date(int year, int month, int day)
 : _year(year)
 , _month(month)
 , _day(day)
 {}
 
private:
 int _year;
 int _month;
 int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
 _cout<<d._year<<"-"<<d._month<<"-"<<d._day;
 
 return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
 _cin>>d._year;
 _cin>>d._month;
 _cin>>d._day;
 
 return _cin;
}
int main()
{
 Date d;
 cin>>d;
 cout<<d<<endl;
 return 0;
 }

注意:
(1)友元函数可访问类的私有和保护成员,但不是类的成员函数
(2)友元函数不能用const修饰
(3)友元函数可以在类定义的任何地方声明,不受类访问限定符限制
(4)一个函数可以是多个类的友元函数
(5)友元函数的调用与普通函数的调用和原理相同

友元类:友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员
(1)友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
(2)友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。

class Date; // 前置声明
class Time
{
 friend class Date; //声明Date类为Time类的友元类,则可以在Date类中就直接访问Time的成员变量。

5.内部类
简单来说就是class里面在class,嵌套class。
注意:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中
的所有成员。但是外部类不是内部类的友元。

class A
{
private:
 static int k;
 int h;
public:
 class B
 {
 public:
 void foo(const A& a)
 {
 cout << k << endl;//OK
 cout << a.h << endl;//OK
 }
 };
};
int A::k = 1;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杜杜_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值