目录
C语言关注的是过程,分析求解问题的步骤,通过函数调用逐步解决问题;
C++关注的是对象,将一件事拆分成不同对象,靠对象间交换完成;
一,类定义
- 在C中,结构体中只能定义变量;
- 但C++中,结构体内不仅可以定义变量,还可定义函数;更喜欢用类(class)来替代;
//struct内可定义变量和函数
struct S
{
void fun(){cout << "class" << endl;}
char _name[20];
int _age[3];
};
int main()
{
S s; //C++中将结构体看成特殊的类
s.fun();
return 0;
}
类是 C++ 的核心特性,用于指定对象的形式(即描述对象),通常被称为用户定义的类型;定义一个类,本质上是定义一个数据类型的蓝图;
类定义:class(类关键字) + classname(类名字) + {}(类主体) + ;
- 类中的元素称为类的成员;
- 类中的数据—类属性/成员变量;
- 类中的函数—类方法/成员函数;
类定义的两种方式:
- 声明和定义全部放在类体中,需注意,成员函数如在类中定义,编译器可能会将其当成内联函数处理;
- 类声明放在.h文件,类定义放在.cpp文件中,一般采用此种方式;
注:
- 成员函数的定义,即函数的实现;
- 成员变量的定义,即开区间;
//类声明和定义全部放在类体中
//编译器可能会将成员函数当成内联函数处理
class Person
{
public: //访问修饰符:private/public/protected
//方法或成员函数
void showInfo(){cout << _name << "/" << _sex << "/" << _age << endl;}
public: //访问修饰符:private/public/protected
//成员变量
char* _name;
char* _sex;
int _age;
}; //封号结束
//.h 声明
class Person
{
public:
void showInfo();
public:
char* _name;
char* _sex;
int _age;
};
//.cpp 定义
void Person::showInfo()
{
cout << _name << "/" << _sex << "/" << _age << endl;
}
二,类的访问限定符及封装
C++实现封装的方式,用类将对象的属性与方法结合在一起,通过访问权限选择性的将其接口提供给外部的用户使用;访问限定符是限制类成员的访问,包括成员变量和成员函数;
访问类成员运算符
- 点运算符(.),适用类对象;
- 箭头运算符(->),适用于类对象指针;
访问限定符
- public,在类内外均可直接访问;
- private,仅在类内访问,类外无法访问,派生类也无法访问;
- protected,类似private,但派生类可访问;
- 访问权限作用域从该访问限定符出现位置开始直到下一个访问限定符出现为止;
- class默认访问权限为private,struct则为public(需兼容C);
注:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别;
C++中struct和class的区别:
- C++需兼容C,所以C++中struct可当成结构体使用,此外struct还可用来定义类;
- 和class定义类一样,struct默认访问权限为public,class为private;
封装
面向对象三大特征:封装、继承、多态、抽象、反射(java)等;
- 封装:将数据和方法进行有机结合,通过访问限定符来隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互;
- 封装本质是管理;
三,类的作用域
- 类定义了一个新的作用域,类的所有成员都在类的作用域中;
- 在类外定义成员,需使用作用域限定符(::),来指明成员属于的类域;
//声明
class Person
{
public:
void showInfo();
public:
char _name[20];
char _sex[3];
int _age;
};
//类外定义
void Person::showInfo()
{
cout << _name << "/" << _sex << "/" << _age << endl;
}
作用域限定符::在运算符中等级最高,分三种:
- 全局作用域符,::name;
- 命名空间作用域符,namespace::name;
- 类作用域符,class::name;
注,{ }即可认为是一个域;
四,类的实例化
用类类型创建对象的过程,即称为类的实例化;
- 类像个模型,限定了其成员,定义一个类并没有分配实际内存空间来存储;
- 一个类可以实例化多个对象,实例化出的对象,占用实际的物理空间;
- 类就像实际图纸,类实例化就是使用图纸建造出来;
实例化格式
- [class] + 类名 + 对象名,自动调用默认无参构造函数,class可省略
- ClassName obj
- [class] + 类名 + 对象名(参数),显示调用有参构造函数,class可省略
- ClassName obj(params)
//类的实例化
Person man;
Person woman;
匿名对象
- 创建对象时没有指定对象名称的声明方式;
- 匿名对象没有命名,只能创建后立即使用;
匿名对象的使用:
- 用于简单的临时计算结果,可直接调用成员函数来访问成员变量;
- 可作为函数实参、返回值、常量赋值、或作为其他表达式的操作数;
class A
{
public:
//构造函数
A(int a=1, int b=1)
:_a(a), _b(b)
{
cout << "A(int a, int b)" << endl;
}
//拷贝构造函数
A(const A& a)
{
_a = a._a;
_b = a._b;
cout << "A(const A& a)" << endl;
}
int get() const { return _a; }
void print(A a) { cout << a._a << endl; }
const A& printf() { return A(); }
private:
int _a;
int _b;
};
int main()
{
A().get(); //简单计算,简化代码
A a = A(11, 12); //常量赋值,编译器优化省略拷贝构造,直接转化为A a(11,12)
a.print(A()); //作为函数参数
a.printf().get(); //作为返回值
return 0;
}
注:内置类型变量初始化方式
//以下三种方式效果相同 int a=1; //直接赋值 int a(1); //括号初始化 int a{1}; //花括号初始化,C++11引入 int a(); //编译器会解析为函数声明
五,类对象大小
类大小
- 即该类中“成员变量”之和;
- 需内存对齐;
- 空类,比较特殊,编译器会给一个字节来唯一标识此类,此字节不存储有效数据;
类对象的存储方式
- 对象包含所有类成员,对象成员变量是不同的,但却调用同一函数,如每一个对象函数都会存一份代码,浪费空间;
- 只保存成员变量,成员函数存放在公共代码段;
- 在多重继承和虚继承等情况下,对象存储方式可能会有所不同;此外还有一些特殊类型的对象有其特定的存储方式,如动态分配的对象和位域对象;
数据成员存储
- 创建对象时,在对象内存中分配内存存储数据成员,可通过指针或引用访问;
- 对象大小取决于其数据成员的数量和类型;
- 如有虚函数,对象内存中还会存储一个指向虚函数表的指针;
函数成员存储
- 成员函数不作为对象的一部分存储,是在程序代码段中定义(除内联函数可能在对象代码中展开);
- 成员函数可访问对象数据成员和其他成员函数,但不可访问自身地址;
注:结构体内存对齐规则
- 第一个成员偏移量为0;
- 其他成员变量要对齐到对齐数的整数倍的地址处;
- 结构体总大小,最大对齐数的整数倍;
- 如嵌套结构体,嵌套结构体对齐到自己的最大对齐数的整数倍,结构体总大小为最大对齐数的整数倍(含嵌套结构体的对齐数);
- 对齐数=编译器默认对齐数和该成员大小的较小值,vs默认为8;
六,this指针
- 成员函数体中,没有关于不同对象的区分;不同对象调用成员函数时,C++通过引入this指针来解决;
- C++编译器给每个“非静态的成员函数”增加一个隐藏的指针参数,让该指针指向当前对象;
- 在函数体中,所有成员变量的操作,都是通过该指针去访问的;只是所有操作对用户是隐藏的,无需用户传递,编译器自动完成;
this指针的特性
- this指针的类型,类类型* const ;
- this指针只能在“成员函数”的内部使用;
- this指针本质上其实是一个成员函数的形参,对象调用成员函数时,将对象地址作为实参传递给this形参,this指针存于栈帧中;
- this指针是成员函数第一个隐含的指针形参,vs编译器一般通过ecx寄存器自动传递,无需用户传递;
//定义一个类
class Date
{
public:
void Display()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
Display();
}
private:
int _year;
int _month;
int _day;
};
//实际成员函数
void Display(Date* this)
{
cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
}
void SetDate(Date* this, int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
this->Display();
}
//此程序可正常运行
//因为成员函数地址存于公共代码段
//调用函数时不会去访问p指向的空间,不存在空指针的解引用
//只会把p传递给隐形的this指针,且show函数内也没有解引用this指针
class A
{
public:
void show()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->show();
return 0;
}