欢迎来到程序员餐厅
今日主菜:类和对象
主厨:邪王真眼
所属专栏:c++专栏
主厨的主页:Chef‘s blog
前言:
咱们之前也是打开了c++的大门,相信很多朋友对这门新的语言都充满了好奇,那么今天就来份硬菜满足一下各位,铛铛铛铛,c++第一座大山:类和对象,他来了!
【本节目标】
- 1.面向过程和面向对象初步认识
- 2.类的引入
- 3.类的定义
- 4.类的访问限定符及封装
- 5.类的作用域
- 6.类的实例化
- 7.类的对象大小的计算
- 8.类成员函数的this指针
1.面向过程和面向对象初步认识
- C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
- C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
看不明白没关系,这里只是走过场说一下,具体后面学习时就懂了。
2.类的引入
定义变量
定义变量,也可以定义函数
。
typedef int DataType;
struct Stack
{
void Init(size_t capacity)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(const DataType& data)
{
// 扩容
_array[_size] = data;
++_size;
}
DataType Top()
{
return _array[_size - 1];
}
void Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
DataType* _array;
size_t _capacity;
size_t _size;
};
int main()
{
Stack s;
s.Init(10);
s.Push(1);
s.Push(2);
s.Push(3);
cout
3.类的定义
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
为
定义类的
关键字,
ClassName
为类的名字,
{}
中为类的主体,
类定义结束时
后面分号不能省略
。
类的成员:
类中的
变量
称为
类的属性
或
成员变量
;
类中的
函数
称为
类的方法
或者
。
3.1类的两种定义方式:
3.1.1. 声明和定义全部放在类体中。
在类中定义
,编译器可能会将其当成
内
联函数
处理。
3.1.2. 声明和定义分开
类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
3.2成员变量命名规则的建议:
// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
void Init(int year)
{
// 这里的year到底是成员变量,还是函数形参?,其实两个year都是形参,可以参考全局和局部变量同名,局部优先原则
year = year;
}
private:
int year;
};
// 所以一般都建议这样
class Date
{
public:
void Init(int year)
{
_year = year;
}
private:
int _year;
};
// 其他方式也可以的,主要看公司要求。一般都是加个前缀或者后缀标识区分就行
4.类的访问限定符及封装
4.1 访问限定符
实现封装的方式:
用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选
- 1. public修饰的成员在类外可以直接被访问
- 2. protected和private修饰的成员在类外不能直接被访问(目前可以将protected等同于private)
- 3. 访问权限作服务器托管网用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 4. 如果后面没有访问限定符,作用域就到 } 即类结束。
- 5. class的默认访问权限为private,struct为public(因为struct要兼容C)
C++
需要兼容
C
语言,所以
C++
中
struct
可以当成结构体使用。另外
C++
中
struct
还可以用来
class
定义类是一样的,区别是
struct
定义的类默认访问权限是
public
,
class
定义的类
private
。注意:在继承和模板参数列表位置,
struct
和
class
也有区别,后序给大
4.2 封装
封装、继承、多态
。
插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU
、显卡、内存等一些硬件元件。
CPU
内部是如
计
。
5.类的作用域
,类的所有成员都在类的作用域中
。
在类体外定义成员时,需要使用
::
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout
6.类的实例化
类是对对象进行描述的
,是一个
模型
一样的东西,限定了类有哪些成员,定义出一个类
并没
一个类可以实例化出多个对象,
实例化出的对象
占用实际的物理空间,存储类成员变量
做个比方。
类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图
,只设
7.类对象模型
7.1 如何计算类对象的大小
class A
{
public:
void PrintA()
{
cout
类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算
回答:只存储成员变量,成员函数存放在公共的代码段
// 类中既有成员变量,又有成员函数
class A1 {
public:
void f1(){}
private:
int _a;
};
// 类中仅有成员函数
class A2 {
public:
void f2() {}
};
// 类中什么都没有---空类
class A3
{};
____4
__ sizeof(A2) :
___1_
__ sizeof(A3) :
__1__
__
”
成员变量
”
之和,当然要注意内存对齐
8.this指针
8.1 this指针的引出
Date
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout
类中有
Init
与
两个成员函数,那当
d1
调用
Init
函 数时,该函数是如何知道应该设置d1
对象,而不是设置
d2
对象呢?
C++
中通过引入
this
指针解决该问题,即:
C++
编译器给每个
“
非静态的成员函数
“
增加了一个隐藏
的指针参数,让该指针指向当前对象
(
函数运行时调用该函数的对象
)
,在函数体中所有
“
成员变量
”
的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来进行相关操作,编
译器自动完成
。
8.2 this指针的特性
- 1. this指针的类型:类 类型* const,即成员函数中,不能给this指针赋值。
- 2. 只能在“成员函数”的内部使用
- 3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针。
- 4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递
- 1. this指针存在哪里?
- 2. this指针可以为空吗
回答1:在栈区,因为他是形参/vs下在ecx寄存器中
回答2:可以,具体看一下代码
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout Print();
return 0;
}
这是因为Print函数在代码段,不在p所指向的空间,因此不需要解引用,所以可以使空指针。
class A
{
public:
void init(int a)
{
_a = a;
}
void Print()
{
cout init(2);
return 0;
}
这段代码却会崩溃,因为init函数要调用成员变量_a,对象成员变量在p所指向的空间,可是p是空指针,解引用会导致程序崩溃。
8.3. C语言和C++实现Stack的对比
8.3.1. C语言实现
typedef int DataType;
typedef struct Stack
{
DataType* array;
int capacity;
int size;
}Stack;
void StackInit(Stack* ps)
{
assert(ps);
ps->array = (DataType*)malloc(sizeof(DataType) * 3);
if (NULL == ps->array)
{
assert(0);
return;
}
ps->capacity = 3;
ps->size = 0;
}
void StackDestroy(Stack* ps)
{
assert(ps);
if (ps->array)
{
free(ps->array);
ps->array = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
void CheckCapacity(Stack* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity * 2;
DataType* temp = (DataType*)realloc(ps->array,
newcapacity*sizeof(DataType));
if (temp == NULL)
{
perror("realloc申请空间失败!!!");
return;
}
ps->array = temp;
ps->capacity = newcapacity;
}
}
void StackPush(Stack* ps, DataType data)
{
assert(ps);
CheckCapacity(ps);
ps->array[ps->size] = data;
ps->size++;
}
int StackEmpty(Stack* ps)
{
assert(ps);
return 0 == ps->size;
}
void StackPop(Stack* ps)
{
if (StackEmpty(ps))
return;
ps->size--;
}
DataType StackTop(Stack* ps)
{
assert(!StackEmpty(ps));
return ps->array[ps->size - 1];
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->size;
}
int main()
{
Stack s;
StackInit(&s);
StackPush(&s, 1);
StackPush(&s, 2);
StackPush(&s, 3);
StackPush(&s, 4);
printf("%dn", StackTop(&s));
printf("%dn", StackSize(&s));
StackPop(&s);
StackPop(&s);
printf("%dn", StackTop(&s));
printf("%dn", StackSize(&s));
StackDestroy(&s);
return 0;
}
C
语言实现时,
Stack
相关操作函数有以下共性:
- 每个函数的第一个参数都是Stack*
- 函数中必须要对第一个参数检测,因为该参数可能会为NULL
- 函数中都是通过Stack*参数操作栈的
- 调用时必须传递Stack结构体变量的地址
- 结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据 的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出 错。
8.3.2. C++实现
typedef int DataType;
class Stack
{
public:
void Init()
{
_array = (DataType*)malloc(sizeof(DataType) * 3);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = 3;
_size = 0;
}
void Push(DataType data)
{
CheckCapacity();
_array[_size] = data;
_size++;
}
void Pop()
{
if (Empty())
return;
_size--;
}
DataType Top(){ return _array[_size - 1];}
int Empty() { return 0 == 服务器托管网_size;}
int Size(){ return _size;}
void Destroy()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
void CheckCapacity()
{
if (_size == _capacity)
{
int newcapacity = _capacity * 2;
DataType* temp = (DataType*)realloc(_array, newcapacity *
sizeof(DataType));
if (temp == NULL)
{
perror("realloc申请空间失败!!!");
return;
}
_array = temp;
_capacity = newcapacity;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
int main()
{
Stack s;
s.Init();
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
printf("%dn", s.Top());
printf("%dn", s.Size());
s.Pop();
s.Pop();
printf("%dn", s.Top());
printf("%dn", s.Size());
s.Destroy();
return 0;
}
中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在
,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。
Stack*
的参数了,编译器编译之后该参数会自动还原,即
C++
中
Stack *
C
语言中需用用户自己维护。
结语:
ok,我们和“类和对象”的第一战打完了,接下来还有两场两场苦战,c++刚开始学会有刚学c时的感觉——啥也不明白,但只要把语法搞定,你一定会发现c++用着比c爽很多的,加油!!!
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
深度学习的进展 深度学习作为人工智能领域的重要分支之一,利用神经网络模拟人类大脑的学习过程,通过数据训练模型以自动提取特征、识别模式、进行分类和预测等任务。近年来,深度学习在多个领域取得显著进展,尤其在自然语言处理、计算机视觉、语音识别和机器翻译等方面实现了突…