目录
1.类
1.2 类的访问限定符及封装
C++中struct和class的区别是什么?
2.1 类的实例化
2.2 类对象的大小
3.this指针
4.类的6个默认成员函数
4.2 构造函数
4.3 构造函数的缺陷
1.类
C++将struct升级成为“类”,即使结构体,也是类。类中有:成员变量和成员函数
其中C++结构体内不仅可以定义变量,也可以定义函数(类中参数不需要传,可以直接访问)。
struct Stack
{
int* a;
int capacity;
int top;
void Init()
{
a = 0;
top = capacity = 0;
}
void Push()
{
//
}
};
int main()
{
Stack s1;
s1.Init();
s1.Push();
return 0;
}
注:C++升级成类后,类名Stack同时也是类型
C++的用法(C语言中哪怕typedef内部还是要加struct)
struct ListNode
{
ListNode* next;
int val;
};
1.2 类的访问限定符及封装
当换成class类后,再次编译发现说无法访问私有(private)
这是因为:class的默认访问限定符权限为private,struct为public(因为struct要兼容C)
其中,访问限定符权限分为:公有(public),私有(private),保护(protected)
公有:类外/中可以访问 私有:只能在类中访问(保护与私有一样)
想给别人使用的定义成公有
class Stack
{
int* a;
int capacity;
int top;
public:
void Init()
{
a = 0;
top = capacity = 0;
}
private:
void Push(int a)
{
//
}
};
成员变量开始为私有,直到public成员函数变为公有,一直到结束(除非遇到下一个访问限定符,此时Push无法访问)
类也是一个域(花括号括起来就是一个域,一般影响的是编译器搜索规则)
一般默认类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:在类体外定义成员时,成员函数名前需要加类名::(方便阅读代码)
需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
所以我们可以将小函数放到类中定义,大函数声明和定义分离,让定义和声明分离(成员函数就默认不是内联函数,同时也没办法在声明中加inline)
C++中struct和class的区别是什么?
C++兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和 class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是 private。
2.1 类的实例化
变量的声明和定义的区别:声明没开空间,定义开空间。声明只是告诉编译器有这个东西
用类型去定义一个对象的时候,才会开空间,叫做类的实例化(用类的类型创建对象的过程)
int top;//定义,全局变量
class Stack
{
int* a;//声明,没开空间
int capacity;
int top;
};
int main()
{
Stack s1;//类的实例化
return 0;
}
类的注意事项:
1.类是对 “对象” 进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它(类不占空间),实例化出对象才占空间
2.类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
class Person
{
private:
char _name[20];
char _gender[3];
int _age;
};
int main()
{
Person._age = 100;//报错
return 0;
}
全局变量使用static改变的是连接属性仅在当前文件可见
2.2 类对象的大小
sizeof不计算成员函数的大小,只计算成员变量大小,实际就是该类中”成员变量”之和,注意内存对齐
class Mi
{
public:
void fun()
{
cout << " Mi " << endl;
}
char a;
};
int main()
{
cout<
当类中只有成员函数时,占1个字节;空类的大小比较特殊,编译器给了空类一个字节来唯一标识这个类的对象
class Mi
{
public:
void fun()
{
cout << " Mi " << endl;
}
};
class Mi1
{};
int main()
{
cout<
类对象的存储方式
对象里面只保存成员变量,成员函数存放在公共的代码段
以下函数的运行结果?
class Mi
{
public:
void fun()
{
cout << " Mi " << endl;
}
char a;
};
int main()
{
Mi* ptr = nullptr;
ptr->fun();
return 0;
}
该程序正常运行,原因在于调用fun,并不会去对象内部找成员函数,并没有去对空指针解应用,而是在编译链接(链接错误就是符号表找不到地址)时就根据函数名找到了函数的地址
3.this指针
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成
下列为this指针隐藏的内容this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。this指针只能在“成员函数”的内部使用
下列程序运行的结果是什么?
class A
{
public:
void PrintA()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
由于有this指针存在,其实传递过去了this指针,隐藏了this->_a,对空指针进行解应用,程序崩溃
this指针存在哪里?
默认存在栈区,偶尔也会存在寄存器上(当有很多成员变量时怕,频繁访问this指针,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递)。this指针本质上是“成员函数”第一个隐含的指针形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。 所以对象中不存储this指针
4.类的6个默认成员函数
任何类在什么都不写时,编译器会自动生成6个默认成员函数(用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数,一旦用户显式定义编译器将不再生成),它们分别为:
初始化和清理:构造函数--完成初始化 析构函数--完成清理工作
复制拷贝:拷贝构造--使用同类对象初始化创建对象 赋值重载--把一个对象赋值给另一个对象
+运算符重载
取地址重载:普通对象和const对象取地址
4.2 构造函数
构造函数是特殊的成员函数,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
其特性:
1.函数名与类名相同。
2.无返回值(不用写void)
3.对象实例化时编译器自动调用对应的构造函数。
4.构造函数可以重载
5.参数看自己需要
普通初始化函数
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
构造函数的性质
1.保证对象实例化的时候一定被初始化
2.调全缺省的和无参的构造函数都可以,但是会导致歧义,所以一般第一第二个构造函数默认写一个
3.默认写一个全缺省参数(第二个函数)
4.如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
class Date
{
public:
Date()//无参构造函数
{
_year = 0;
_month = 0;
_day = 0;
}
Date(int year=0, int month=0, int day=0)//全缺省
{
_year = year;
_month = month;
_day = day;
}
Date(int year, int month, int day)//要写参数,带参构造函数
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 7, 22); // 调用带参的构造函数
//Date d2; // 调用无参构造函数
Date d3(2000);
return 0;
}
4.3 构造函数的缺陷
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,但是实际我们不写构造函数却发现编译器并没有实际初始化对象
原因在于:C++把成员变量分为两种类型:1.内置类型(语言自带) 2.自定义类型 (struct/class类类型)
默认生成的构造函数对内置类型不做处理;对自定义类型进行处理,会去调用他的默认构造函数
任意类型的指针都属于内置类型,例如Time *_t(类型是解应用时意义)
这也就意味着,默认生成的构造函数对自定义类型无价值
举个例外,例如代码中MyQueue有价值(原因在于:类中的成员全是自定义类型才有价值)
MyQueue中没有写构造函数却被初始化,这是由于不写MyQueue构造函数的时候,对自定义类型会去调用它的默认构造函数(也依赖Stack构造函数)
typedef int DataType;
class Stack
{
public:
Stack(int capacity=4)
{
cout << "Stack(int capacity = 4)" << endl;
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_size = 0;
_capacity = capacity;
}
void Push(DataType data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
private:
DataType* _array;
int _capacity;
int _size;
};
class MyQueue
{
Stack s1;
Stack s2;
};
int main()
{
Stack st;
MyQueue q;
return 0;
}
C++11打了补丁,允许在成员变量处赋值,不是初始化(这里还是声明),而是给编译器默认构造函数的缺省值(如果没有显示传参就用它们初始化,显示写了构造函数就没他事了)
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
以下三个都可以叫默认构造函数:
1.我们不写,编译器自动生成的那个
2.我们自己写的全缺省构造函数
3.自己写的无参构造函数
三个默认构造函数特点:不传参数就可以调用