目录
[2.2 对象大小](#2.2 对象大小)
1.1类的定义格式
class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数
c++中也可以定义struct ,c++兼容c语言 但是对比class 比较推荐class 的定义类里面的成员函数默认是inline
1.2访问限定符
public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private是⼀样的,以后继承章节才能体现出他们的区别
访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌,如果后⾯没有访问限定符,作⽤域就到}即类结束。
class定义成员没有被访问限定符修饰时默认为private,struct默认为public 。一般成员变量都会被限制为private/protected,需要给别⼈使⽤的成员函数会放为public。

1.3类域
类的所有成员都在类的作⽤域中,在类体外定义成员时,需要使⽤::作⽤域操作符指明成员属于哪个类域类域
影响的是编译的查找规则,下⾯程序中Init如果不指定类域Stack,那么编译器就Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪⾥,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找

2、实例化
2.1实例化概念
⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。 类是对象进⾏⼀种抽象描述,是⼀个模型⼀样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,⽤类实例化出对象时,才会分配空间
⼀个类可以实例化出多个对象,实例化出的对象 占⽤实际的物理空间,存储类成员变量

2.2 对象大小
内存对⻬规则 (与c语言的相似)
第⼀个成员在与结构体偏移量为0的地址处。
其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
注意:对⻬数=编译器默认的⼀个对⻬数与该成员⼤⼩的较⼩值。
VS中默认的对⻬数为8
结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
如果嵌套了结构体的情况,嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处,结构体的整体⼤⼩
就是所有最⼤对⻬数(含嵌套结构体的对⻬数)的整数倍
上⾯的程序运⾏后,我们看到没有成员变量的B和C类对象的⼤⼩是1,为什么没有成员变量还要给1个字节呢?因为如果⼀个字节都不给,怎么表⽰对象存在过呢!所以这⾥给1字节,纯粹是为了占位标识对象存在。

3、this指针
Date类中有Init与Print两个成员函数,函数体中没有关于不同对象的区分,那当d1调⽤Init和
Print函数时,该函数是如何知道应该访问的是d1对象还是d2对象呢?那么这⾥就要看到C++给了 ⼀个隐含的this指针解决这⾥的问题
编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this指针。⽐如Date类的Init的真实原型为,void Init(Date* const this, int year, int month, int day)
类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值this->_year = year;
C++规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理),但是可以在函数体内显⽰使⽤this指针。
就那上一个为例 void Init() 看似这里没有参数 可是这里被隐藏了一个指针 那就是this指针 那么应该是void Init( Date * this )

下面可以看两道题来练练手
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
}
private:
int _a;
};
int mar4t6in()
{
A* p = nullptr;
p->Print();
return 0;
}
好了宣布答案
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;//输出这个A::Print()
}
private:
int _a;
};
int mar4t6in()
{
A* p = nullptr;
p->Print();// 不是对空指针的引用,这里的结果是 运行正常
return 0;
}
那么我在加一句代码结果是什么样子呢?
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;//输出这个A::Print()
cout <<_a<<endl;
}
private:
int _a;
};
int mar4t6in()
{
A* p = nullptr;
p->Print();// 不是对空指针的引用,这里的结果是 运行正常
return 0;
}

结果就是程序报错
3.1.类的默认成员函数
默认成员函数就是⽤⼾没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。
我们不写函数编译器自动会生成6中默认函数 初始化和清理(构造函数和析构函数) 、拷贝复制(拷贝构造和赋值重载) 、取地址重载 重要的是前4个
从俩个方面学习:
1、 我们不写时,编译器默认⽣成的函数⾏为是什么,是否满⾜我们的需求。
2、 编译器默认⽣成的函数不满⾜我们的需求,我们需要⾃⼰实现,那么如何⾃⼰实现?
3.2构造函数
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象(我们常使⽤的局部对象是栈帧创建时,空间就开好了),⽽是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能,构造函数⾃动调⽤的特点就完美的替代的了Init。
**构造函数的特点:
- 函数名与类名相同。
- ⽆返回值。(返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)
- 对象实例化时系统会⾃动调⽤对应的构造函数。
- 构造函数可以重载。
- 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦⽤⼾显
式定义编译器将不再⽣成。 - ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函
数。但是这三个函数有且只有⼀个存在,不能同时存在。⽆参构造函数和全缺省构造函数虽然构成
函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫
默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调
⽤的构造就叫默认构造。 - 我们不写,编译器默认⽣成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始
化是不确定的,看编译器。对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数初始
化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要⽤
初始化列表才能解决,初始化列表,我们下个章节再细细讲解。
说明:C++把类型分成内置类型(基本类型)和⾃定义类型。内置类型就是语⾔提供的原⽣数据类型,
如:int/char/double/指针等,⾃定义类型就是我们使⽤class/struct等关键字⾃⼰定义的类型**。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdlib.h>
using namespace std;
//定义的类
class push
{
public :
inline void Init( )
{
cout << "今晚就发财" << endl;
}
private://成员限定访问符
int age;
int size;
};
int mai423n()
{
push s1;
s1.Init();
//push::Init();
return 0;
}
class stack
{
public :
void init1( int n =4);
private:
int* _arr;
size_t _capacity;
size_t _top;
};
//在类域外面定义要用:: 这个引用
void stack::init1( int n )
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == nullptr)
{
perror("mallco fail ");
exit(1);
}
_capacity = n;
_top = 0;
}
int mai245n()
{
stack s1;
s1.init1();
return 0;
}
// 实例化对象
class Date
{
public :
void Init(int y = 2018, int m = 9, int d = 4)
{
_year = y;
_month = m;
_day = d;
}
void Print()
{
cout <<_year << "/" << _month<< "/" << _day << endl;
}
private :
int _year;
int _month;
int _day;
};
int ma3win()
{
//对象实例化可以有多个 Date 实例化出多个对象
Date d1;
Date d2;
d1.Init();
d1.Print();
d2.Init(2034, 3, 6);
d2.Print();
return 0;
}
//对象大小
class A
{
private:
int a;
int n;
int d;
};
//这里只有一个 那么会是多少
class B
{
void Print();
};
//那么这里什么都没有 有会是多少呢?
class C
{
};
int mai35rn()
{
cout << sizeof(A) << endl;//这里有三个整形 所以就是12
cout << sizeof(B) << endl;//这里开辟了一个空间就算一个
cout << sizeof(C) << endl;//当然这里也是
return 0;
}
//this指针
class f
{
public :
//这里的展开是 void Init ( Date * this int y=2013,int m=34 ,
// int d=12)
void Init(int y=2013,int m=34 , int d=12)
{
_year = y;
_month = m;
_day = d;
//cout << "对了 "<<endl;
}
void print()
{
cout << _year << '-' << _month << '-' << _day<<endl;
}
private :
int _year;
int _month;
int _day;
};
int main32()
{
f f1;
f1.Init();
f1.print();
return 0;
}
练习题
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;//输出这个A::Print()
}
private:
int _a;
};
int mar4t6in()
{
A* p = nullptr;
p->Print();// 不是对空指针的引用,这里的结果是 运行正常
return 0;
}
//那么我在这个的基础上在加入一句语句呢?
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;//输出这个A::Print()
cout << _a << endl;//这里就是对空指针的引用, 就会发生程序报错
}
private:
int _a;
};
int main324()
{
A* p = nullptr;
p->Print();// 不是对空指针的引用,这里的结果是 运行正常
return 0;
}
//构造函数
class Date
{
public :
//Date( )
//{
// _year = 1;
// _month = 1;
// _day = 1;
//}
Date( int year=10 ,int month=20 ,int day=30)
//当函数是无参数的时候这里不可以用全缺省 因为编译器也不知道要调用哪个
{
_year = year;
_month = month ;
_day = day;
}
void Print()
{
cout << _year << '-' << _month << '-' << _day << endl;
}
private :
int _year;
int _month;
int _day;
};
int main()
{//对象实例化是无法调用
//Date f1 (); 如果这里的构造函数加了一个括号那么就
// 分不清楚是构造函数还是函数声明了
Date d1;
Date d2(2031,3,4);
d1.Print();
d2.Print();
Date d3(202);
d3.Print();
return 0;
}
class Date
{
public :
Date()
{
cout << _year << _month << _day << endl;
}
void Print()
{
cout << _year << _month << _day << endl;
//编译器对内置类型初始化没有要求
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
return 0;
}