c语言就是面向过程的语言,即关注的是过程的求解,我们利用函数逐步解决问题。
c++是面向对象的语言。将一件事情拆分成不同的对象,靠的是对象之间的关系完成。
c++中结构体不仅可以定义变量,还能定义函数,而在c语言中结构体只能定义变量。现在以c++方式实现,发现struct也可以定义函数。
class ClassName
{
//由成员函数和成员变量组成
};
类体中的内容称为类的成员,类的变量称为类的属性或成员变量,类中的函数称为类的方法或成员函数。
1类的声明和定义全部放在类体当中,需要注意的是,如果函数在类中定义,编译器可能会将其当作内联函数处理。
class Person
{
public:
void ShowPeoInfo()
{
cout<<_name<<"-"<<_age<<"-"<<_sex<<endl;
}
public:
char* _name;
char* _sex;
int age;
};
2类的声明放在头文件中,类的函数放在源文件当中。注意:成员函数之前需要加类名::
声明放在person.h文件当中
class Person
{
public:
void ShowPeoInfo();
public:
char* _name;
char* _sex;
int age;
}:
person.cpp
注意:成员函数前需要加上类名
#inlcude<person.h>
void Person::ShowPeoInfo()
{
cout<<_name<<"-"<<_age<<"-"<<_sex<<endl;
}
成员命名规则的建议:
Class Date
{
public:
void Init()
{
year=year;
}
private:
int year;
};
这里有一个问题,那就是这里的year到底是成员变量还是函数形参?
我们应该用如下方式命名变量:
Class Date
{
public:
void Init()
{
year=_year;
}
private:
int _year;
};
c++用类的方法将对象的属性和方法放在一起,通过选择性地将接口提供给外部使用来实现封装。
访问限定符的作用域是从这个访问限定符开始到下一个访问限定符
class的默认访问权限为private,struct的默认访问权限为public。
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符的区别。
面向对象的三大特点:封装,继承,多态
封装:将数据以及操作数据的方法有机结合,隐藏对象的属性和实现细节,仅对外提供接口来和对象进行交互。
类定义了一个新的作用域,在类体外定义成员时,需要用域作用限定符::指明成员属于哪个类域。
用类类型创建对象的过程,称为类的实例化。
1 类是对对象进行描述的,定义出一个类并没有实际的空间储存它
2 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量
int main()
{
Person._age=100;
return 0;
}
// 编译失败:error C2059: 语法错误:“.”
Person类是没有空间的,只有person实例化出的对象才有物理空间。
类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?
结论:一个类的大小,就是成员变量之和,注意内存对齐。
空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
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;
}
这里有一个问题,Date中有两个成员函数,Init和Print,函数体中没有不同对象的区分,那么当d1调用Init函数时候,如何知道不是d2而是d1对象调用的呢?
c++中通过引入this指针来解决这个问题:
c++给每个非静态的函数一个this指针,通过指向当前对象(函数运行时调用的对象),所有成员变量的操作,都是通过指针去访问。只不过所有操作都是编译器自己完成的,不需要用户来操作。
例如:d1.Init(&d1,2022,1,11);会将地址传递过去。
前面还有一个指针:
void Init(int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
1 类类型*const,不能给this指针赋值(const*修饰的是指向的对象,*const修饰的是指针本身)
2 本质上是成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针
3 this指针是成员函数的第一个形参,一般由编译器ecx自动传递
比如Date类的Init类的真实原型为:
void Init(Date* const this,int year,int month,int day)
this指针存在内存的哪个区域?
E一定不对,因为刚刚我们算对象大小的时候就没有算this指针。
选A
下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
空指针不可能是编译错误,最多是运行崩溃。这个题是A正常运行,为什么呢?这里不是有空指针吗?
编译器编译以后,都会转换为汇编指令。 p->Print();这句代码转换成的编译指令是call 地址。地址不在p对象里(成员函数的指针不存到对象里面)。为什么需要p调用呢?因为Print()是内域,是成员函数,编译的时候就知道去Class A里面找,这里不需要取地址,因为p就是对象的地址//mov ecx p(ecx存的是this指针)。把p传给了this,this是空指针。cout<<this<endl;空指针没有解引用,并不会报错。如果是p->_a=1;这个时候成员变量被引用,就崩溃了。
下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
这个题目的答案是B。相当于解引用了。
c语言在实现函数时,操作函数有以下特性:
1每个函数的第一个参数都是Stack*
2函数中必须要对第一个参数检测,第一个参数是NULL
3函数都要通过Stack*操作栈
4 调用时必须穿Stack结构体变量的地址
结构体内只能存放数据的结构,操作数据的方法不能放在结构体当中,即数据和数据操作的方式是分开的。
c++中通过类,可以将数据以及操作数据的方法完美结合,通过访问权限可以控制那些方法在类外的函数可以被调用,即封装。C++中 Stack *是编译器自己维护的,而c语言中是需要用户进行维护的。