C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完
成。
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
C++兼容c语言,结构用法可以继续使用
同时struct也升级成了类,struct中可以定义函数了
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class为定义类的关键字,类型的意思,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
声明和定义全部放在类体中时,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
class Stack
{
public:
// 成员函数
void Init()
{
a = nullptr;
top = capacity = 0;
}
void Push(int x)
{
if (top == capacity)
{
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
a = (int*)realloc(a, sizeof(int) * newcapacity);
capacity = newcapacity;
}
a[top++] = x;
}
int Top()
{
return a[top - 1];
}
private:
// 成员变量
int* a;
int top;
int capacity;
};
类的声明和定义可以分开
Func.h中
class Stack
{
public:
//
void Init();
void Push(int x);
int Top();
private:
//
int* a;
int top;
int capacity;
};
在以前学习时说编译器只会向上查找,那么Init在用a的时候向上是找不到a的,因为类不是只会向上找,是在类域里面找
Func.cpp中
//得有作用域运算符::来告诉编译器Init是栈的成员函数
//编译器默认只在当前域、全局域找,不会到空间域、类域里去找
类定义了一个新的作用域,类的所有成员都在类的作用域中。
在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
void Stack::Init()
{
a = nullptr;
top = capacity = 0;
}
void Stack::Push(int x)
{
if (top == capacity)
{
size_t newcapacity = capacity == 0 ? 4 : capacity * 2;
a = (int*)realloc(a, sizeof(int) * newcapacity);
capacity = newcapacity;
}
a[top++] = x;
}
int Stack::Top()
{
return a[top - 1];
}
成员变量命名风格的建议:
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year; // year_ mYear
int _month;
int _day;
};
域搜索:
class Date
{
public:
void Init(int year)
{
year = year;
}
private:
int year;
};
两个year,一个是局部域的year,一个是类域的year,局部优先,先在局部搜索,所以就是自己赋值给自己了,所以成员变量可以加个_方便区分,前加后加都可以,前面加m的也有,member表示成员。
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用
别人如果随便的就能访问成员变量,那么可能会出错,比如下面错误的代码,应该是要top-1的地方
C++封装体现在想给访问就公有,不想给访问的就放成私有,杜绝随意访问数据出错的可能性。
cout << st.a[st.top] << endl;
让使用者不接触成员变量,用成员函数去实现对应功能
cout << st.Top() << endl;
【访问限定符说明】
类里面不受访问限定符的限制,平时用class更好一些,因为这样就必须给访问限定符
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._year;
return 0;
}
类里面这些看似是定义,其实只是声明,Date._year;报错,外面访问不了成员是因为没有空间,变量定义特点是开空间。Date d;这样才是定义,对象的定义也叫对象的实例化,就会开空间,那么成员变量也开空间了
类在内存中不占空间,可以认为它存在文件系统
用类类型创建对象的过程,称为类的实例化
类是对对象进行描述的
类的本质像设计图,类实例化出对象就像用设计图建造出房子
类不存数据,类实例化的对象才能存数据
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;
d1.Init(2023, 7, 19);
d1.Print();
d1._year;
Date d2;
d2.Init(2023, 7, 19);
d2.Print();
d2._year;
cout << sizeof(Date) << endl;
cout << sizeof(d1) << endl;
return 0;
}
类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算
一个类的大小?
只保存成员变量,成员函数存放在公共的代码段
不是到对象里找函数,是到公共代码区找
class A1 {
public:
void f1() {}
private:
char _ch;
int _a;
};
// 类中仅有成员函数
class A2 {
public:
void f2() {}
};
// 类中什么都没有---空类
class A3
{};
int main()
{
cout << sizeof(A1) << endl;
// 分配1byte,不存储数据,只是占位,表示对象存在过
cout << sizeof(A2) << endl;
cout << sizeof(A3) << endl;
A2 aa2;
A2 aaa2;
cout << &aa2 << endl;
cout << &aaa2 << endl;
return 0;
}
sizeof(A1) = 8
sizeof(A2) = 1
sizeof(A3) = 1
对于空类或仅有成员函数分配1字节是为了能区分开不同对象的区别,如果不分配空间那取地址该怎么办?零字节怎么表示这个对象存在?可以开多个字节但是没必要,只是为了表示对象存在从最小单位去考虑,那么一个字节就可以了
那一个字节存什么不重要,因为空类我们也很难去访问
结构体内存对齐规则
Date类中有 Init 成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
绿色的字体就是编译器增加的指针参数,准确来说是Date* const this,从this不能++可以看出
Init里面有this,print里面也有一个this指针,那他们会不会混?他们俩是不一样的,可以从&year打印结果看出来,不同域中可以定义同名变量,两个year不一样,是两个不同的形参。
Date d1;
d1.Init(2023, 7, 20);
d1.Print(2023);
class Date
{
public:
// this在实参和形参位置不能显示写
// 但是在类里面可以显示的用
void Init(int year, int month, int day)
{
cout << this << endl;
//this->_year = year;
//this->_month = month;
//this->_day = day;
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << this << endl;
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year; // 声明
int _month;
int _day;
};
int main()
{
Date d1;
d1.Init(2023, 7, 20);
d1.Print();
Date d2;
d2.Init(2023, 7, 21);
d2.Print();
return 0;
}
this指针的特性
this指针可以为空吗?
下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
//cout << _a << endl;
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
//p->_a;
return 0;
}
正常运行
p->不代表就是解引用因为函数没有存在对象中。
成员函数不需要在p指针指向的对象里去找,是在公共代码区找,编译器会把p传给this指针,vs下是通过寄存器传递,不是push压栈进去(挺多传参是压栈进去的),因为认为this指针会被经常访问。虽然在函数中有this这个空指针,但是没有解引用
在vs下p->_a;这样写没有报错是因为编译器进行了优化,编译器认为p指向了a但是也没有进行赋值等操作,就没有生成指令,在一些老的编译器上面可能就出错了。p->_a=1;进行解引用就空指针报错了
int main()
{
A* p = nullptr;
(*p).Print();
return 0;
}
这样依旧能正常运行,因为函数没有存在对象中。看起来解引用了,其实没有解引用,编译器会转换为汇编指令,传递this指针
this指针存在哪里?
C语言和C++实现Stack的对比