c语言是一门结构化,面向过程的语言,而c++可以支持c。所以c++并不是一门单纯的基于面向对象的语言,C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计。
面向对象的三大特性:封装,继承,多态。
c语言中struct:
c++中struct:
#include
using namespace std;
//结构体节点的定义
struct Node
{
//不用加struct
Node* next;
int data;
void PushBack(Node* phead, int x)
{
//....
}
}
int main()
{
//无需加struct就可以定义变量
Node* head = NULL;
return 0;
}
总结:
class className
{
//成员变量和成员函数
};
c++将成员变量和成员函数封装到类中,通过访问限定符来规定成员的访问权限,提供哪些接口给外部用户。类的访问限定符有三种:public(公有),protected(保护),private(私有)
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
#include
using namespace std;
class Data
{
public:
void Show();
private:
int _year;
int _month;
int _day;
};
void Data::show()
{
cout << _year << endl;
cout << _month << endl;
cout << _day << endl;
}
将类比作一个建筑图纸,那么对象就是建筑物。定义出一个类并没有分配实际的内存空间来存储它,实例化为对象后才开辟空间。
#include
using namespace std;
class Data
{
public:
void Show();
private:
int _year;
int _month;
int _day;
};
void Data::Show()
{
cout << _year << endl;
cout << _month << endl;
cout << _day << endl;
}
int main()
{
Data d1;
Data d2;
return 0;
}
类对象的大小只包含成员变量所占用的空间,代码共用一份即可。其中类对象的成员也需要满足内存对齐。
结构体内存对齐的规则:
- 第一个成员在偏移量为0的地址处
- 其他成员都要对齐到对齐数的整数倍地址处(对齐数:默认对齐数与成员的最小值)
- 结构体总大小:最大对齐数的整数倍
- 如果嵌套了结构体的话,将该结构体对齐到该结构体中最大对齐数的整数倍
#include
using namespace std;
class Data
{
public:
void Show();
private:
int _year;
int _month;
int _day;
};
void Data::Show()
{
cout << _year << endl;
cout << _month << endl;
cout << _day << endl;
}
int main()
{
Data d1;
Data d2;
d1.Show();
d2.Show();
return 0;
}
在上述代码中,由于成员函数只有一份,那么Show函数是怎么分辨出成员变量是属于哪个对象的呢?
在调用函数时,实际上编译器会将对象的地址当作参数传递给show函数,这个地址保存在this指针里面
- 在函数定义处:实际上为void Data::Show(Data* this),有this指针这个隐含形参
- 在调用处:实际上为d1.Show(&d1), d2.Show(&d2);
只是上述步骤,编译器来执行,不允许程序员显示使用,但是可以在成员函数内部使用this指针
this指针的特性:
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
(*p).Print();
return 0;
}
// 2.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
(*p).PrintA();
return 0;
}
在上述代码中,第一个案例是正确的,程序不会报错,第二个是错误的,因为访问了nullptr指针
看代码不能只看表象,需要看底层,编译器看待
p->PrintA();
- 需要去调用PrintA这个函数,转换为汇编call语句
- 需要传递this指针参数,为nullptr
上面1,2案列在这两步并没有访问nullptr,但是第二个代码错误之处在于,访问了成员变量_a,而对象在nullptr处,所以编译器会报错,但是第一个代码并没有真正的访问nullptr,可以调用反汇编来观察