sketch做网站线框图,做网站用的军事图片,seo整站优化+WordPress,荧光字体制作网站
#x1f525;个人主页#xff1a;guoguoqiang. #x1f525;专栏#xff1a;我与C的爱恋 C语言是面向过程的#xff0c;关注的是过程#xff0c;分析出求解问题的步骤#xff0c;通过函数调用逐步解决问题。 C是基于面向对象的#xff0c;关注的是对象…
个人主页guoguoqiang. 专栏我与C的爱恋 C语言是面向过程的关注的是过程分析出求解问题的步骤通过函数调用逐步解决问题。 C是基于面向对象的关注的是对象将一件事情拆分成不同的对象靠对象之间的交互完成。
一、类的引入
C语言结构体中只能定义变量在C中结构体内不仅可以定义变量也可以定义函数。 C语言方式实现的栈结构体中只能定义变量现在以C方式实现会发现struct中也可以定义函数。
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(%d\n, StackTop(s));
printf(%d\n, StackSize(s));
StackPop(s);
StackPop(s);
printf(%d\n, StackTop(s));
printf(%d\n, StackSize(s));
StackDestroy(s);
return 0;
}可以看到在用C语言实现时Stack相关操作函数有以下共性 每个函数的第一个参数都是Stack* 函数中必须要对第一个参数检测因为该参数可能会为NULL 函数中都是通过Stack*参数操作栈的 调用时必须传递Stack结构体变量的地址 结构体中只能定义存放数据的结构操作数据的方法不能放在结构体中即数据和操作数据的方式是分离开的而且实现上相当复杂一点涉及到大量指针操作稍不注意可能就会出错 1.数据和操作数据的方式是分离开的。 2.数据访问控制是自由的不受控制的。
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(%d\n, s.Top());
printf(%d\n, s.Size());
s.Pop();
s.Pop();
printf(%d\n, s.Top());
printf(%d\n, s.Size());
s.Destroy();
return 0;
}C中通过类可以将数据 以及 操作数据的方法进行完美结合通过访问权限可以控制那些方法在类外可以被调用即封装在使用时就像使用自己的成员一样更符合人类对一件事物的认知。而且每个方法不需要传递Stack*的参数了编译器编译之后该参数会自动还原即C中 Stack *参数是编译器维护的C语言中需用用户自己维护。 1.C中通过类可以将数据以及操作数据的方法进行完美结合通过1访问权限可以控制那些方法在类外可以被调用即封装。 2.控制访问方式愿意给你访问的为公有不愿意给你访问的为私有。 在C中更喜欢用class来代替
二、类的定义
class className
{
// 类体由成员函数和成员变量组成
}; // 一定要注意后面的分号class为定义类的关键字ClassName为类的名字{}中为类的主体注意类定义结束时后面分号不能省略。 类体中内容称为类的成员类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。 类的两种定义方式 声明和定义全部放在类体中需注意成员函数如果在类中定义编译器可能会将其当成内 联函数处理。 类声明放在.h文件中成员函数定义放在.cpp文件中注意成员函数名前需要加类名:: 在我们日常使用时尽量使用第二种。 成员变量命名规则的建议
// 我们看看这个函数是不是很僵硬
class Date
{
public:
void Init(int year)
{
// 这里的year到底是成员变量还是函数形参
year year;//形参一般建议 _year mYear year_
}
private:
int year;
};三、类的访问限定符及封装
1.访问限定符
C实现封装的方式用类将对象的属性与方法结合在一块让对象更加完善通过访问权限选择性的将其接口提供给外部的用户使用。 【访问限定符说明】
public修饰的成员在类外可以直接被访问protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止如果后面没有访问限定符作用域就到 } 即类结束。class的默认访问权限为privatestruct为public(因为struct要兼容C) 访问限定符只在编译时有用当数据映射到内存后没有任何访问限定符上的区别
class Date {
public:void Init(int year, int month, int day) {_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};
int main()
{Date d;d.Init(2024, 4, 4);return 0;
}2.封装
面向对象的三大特性封装、继承、多态。 封装将数据和操作数据的方法进行有机结合隐藏对象的属性和实现细节仅对外公开接口来和对象进行交互。 封装本质上是一种管理让用户更方便使用类。 在C语言中实现封装可以通过类将数据以及操作数据的方法进行有机结合通过访问权限来隐藏对象内部实现细节控制哪些方法可以在类外部直接被使用。想让你看到的就是public不想让你看到的就是 private。 四、类的作用域
类定义了一个新的作用域类的所有成员都在类的作用域中。在类体外定义成员时需要使用 ::作用域操作符指明成员属于哪个类域。
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout _name _gender _age endl;
}五、类的实例化
用类类型创建对象的过程称为类的实例化-----开空间 一个类可以有多个实例化对象类就相当于一个图纸可以用这个图纸建造很多个楼。
类是对对象进行描述的是一个模型一样的东西限定了类有哪些成员定义出一个类并没有分配实际的内存空间来存储它一个类可以实例化出多个对象实例化出的对象 占用实际的物理空间存储类成员变量。
int main()
{
Person._age 100; // 编译失败error C2059: 语法错误:“.”
return 0;
}类实例化出对象就像现实中使用建筑设计图建造出房子类就像是设计图只有实例化出的对象才能实际存储数据占用物理空间。 红色箭头的这一步就完成了类的实例化。
六、类对象模型
1.如何计算类对象的大小
只需计算类成员变量的大小无需计算类成员函数的大小类成员函数在公共代码区
class A
{
public:
void PrintA()
{
cout_aendl;
}
private:
char _a;
};类中既可以有成员变量又可以有成员函数那么一个类的对象中包含了什么如何计算一个类的大小
// 类中既有成员变量又有成员函数
class A1 {
public:
void f1(){}
private:
int _a;
};
// 类中仅有成员函数
class A2 {
public:
void f2() {}//大小为1不存储有效数据占位标识对象被实例化定义出来了。
};
// 类中什么都没有---空类
class A3
{};//大小为1不存储有效数据占位标识对象被实例化定义出来了。sizeof(A1) : 4 sizeof(A2) : 1__ sizeof(A3) : 1__ 结论一个类的大小实际就是该类中”成员变量”之和当然要注意内存对齐 注意空类的大小空类比较特殊编译器给了空类一个字节来唯一标识这个类的对象。 再来回顾一下之前C语言中的内存对齐
第一个成员在与结构体偏移量为0的地址处。其他成员变量要对齐到某个数字对齐数的整数倍的地址处。 注意对齐数 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的对齐数为8结构体总大小为最大对齐数所有变量类型最大者与默认对齐参数取最小的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整 体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。 如何让结构体按照指定的对齐参数进行对齐能否按照3、4、5即任意字节对齐 #prama pack() 七、this指针的引出和特性
我们先来定义一个日期类 Date
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; // 日
};
int main()
{
Date d1, d2;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}C编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数让该指针指向当前对象(函数运行时调用该函数的对象)在函数体中所有“成员变量”的操作都是通过该指针去访问。只不过所有的操作对用户是透明的即用户不需要来传递编译器自动完成。 this指针的特性
this指针的类型类类型* const即成员函数中不能给this指针赋值。this指针不能修改只能在“成员函数”的内部使用this指针本质上是“成员函数”的形参当对象调用成员函数时将对象地址作为实参传递给this形参。所以对象中不存储this指针。this指针是“成员函数”第一个隐含的指针形参一般情况由编译器通过ecx寄存器自动传递不需要用户传递 this指针存在 栈中和ecx寄存器中栈中存放形参和局部变量this指针是形参 this指针可以为空 // 1.下面程序编译运行结果是 A、编译报错 B、运行崩溃 C、正常运行//C
class A
{
public:
void Print()
{
cout Print() endl;
}
private:
int _a;
};
int main()
{
A* p nullptr;
p-Print();//正常运行因为没有访问任何对象的成员变量这里不需要访问通过this指针指示的内存地址。
return 0;
}
// 1.下面程序编译运行结果是 A、编译报错 B、运行崩溃 C、正常运行//B
class A
{
public:
void PrintA()
{
cout_aendl;
}
private:
int _a;
};
int main()
{
A* p nullptr;
p-PrintA();//这里发生了报错因为这里访问a是通过this-a来实现的
return 0;
}感谢大家阅读后续给大家带来构造函数和析构函数有关内容。