目录
一,auto关键字
1-1,auto的使用
1-2,基于范围auto的for循环
二,nullptr的运用
三,C++类的初步学习
3-1,类的引用
3-2,类的访问权限
3-3,类的使用
1,类中函数的定义
2,类中对象的使用
3-4,类对象模型
1,类对象的存储方式
2,类的大小
一,auto关键字
开门介绍:
在C++准有规定中,auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量是由编译器在编译时期推导而得。具体简单运用如下:
#include <iostream>
using namespace std;
int main() {
int a = 0;
int b = a;
auto c = a;
auto d = &a;
auto* e = &a;
//typeid是输出指定数据的类型
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
cout << typeid(e).name() << endl;
return 0;
}
运行图:
可看出,auto可用来存储任意类型,像上面说的,它是一种类型提示符,提示系统声明的变量属于什么类型。
1-1,auto的使用
auto的使用其实不是向上面一样,auto在C++中真正的用武之地在于后面学习的复杂结构中,如C++中迭代器的使用,容器的运用等,比如vector<string>::iterator it = v.begin();结构很复杂,使用auto可直接优化为auto it = v.begin()。(这里只是演示,我们只需明白auto的用法即可,具体深入使用后面的文章会具体讲解)。
auto的使用有着以下注意要素:
1,auto的使用必须初始化。auto是用来简化类型的,系统在编译阶段需要根据auto表达式来推导出auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
2,当用auto声明指针类型时,auto和auto*没有任何区别,但用auto声明引用类型时则必须 加&。因为引用的本质就是指定数据的"别名",类型与之相同,如果不加&,系统将不知是否使用引用引用型,而指针类型是"独特的",系统可以区分。
3,auto可以在同一行定义多个变量,但是这些变量必须是相同的类型,否则编译器将会报错,因为编译器在编译阶段只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
4,auto不支持函数的形参使用和函数的返回值使用,原因是当系统遍历到此函数的形参auto型时,相当于没有初始化一样,而一旦做返回值使用,将会出现很多麻烦。(这里与C++内部的复杂构造有关,在这里就不做过多说明)。
1-2,基于范围auto的for循环
在C++11标准介绍中,考虑到了for循环使用的麻烦,所以C++11中就引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,具体的细节和解说请看以下代码。初学者可能有些听不懂迭代的感念,不过本文这里不做重点,这里我们只需明白下面代码如何用auto使用即可,具体的知识后文会讲解,这里只是为后面打基础。
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
//依次取数组中的所有数据赋值给别名e
//自动判断结束,自动++往后面走
//由于引用的使用,下面的for循环相当与将数组中所有元素的乘2
for (auto& e : array)
e *= 2;
//依次输出数据2 4 6 8 10
for (auto e : array)
cout << e << " ";
}
二,nullptr的运用
nullptr相当于C语言中的NULL,C++专门制定此关键字也是为了弥补C中NULL的不足。C语言中的NULL其实是一个宏,直接将其定义为0,但这样以来都会产生一些不避免的麻烦,因为在C++98标准规定中,0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0,而nullptr的使用完美的解决了这一问题,所以,在后序的置空运用中,我们需改用nullptr。
三,C++类的初步学习
在讲解类之前我们先要明白C++与C基础区别。C语言是面向过程语言,关注的是过程,分析出求解问题的步骤,需要通过函数调用逐步解决问题,而C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成,我们平常设定的常量和变量就是对象。
3-1,类的引用
C++中的类可认为C中的结构体,与之不同的是C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。需注意的是,在类中定义的函数默认是inline,但会不会当初内联函数,还需看编译器,跟inline的原理一样。
struct Stack {
//变量的定义
int* a;
int top;
int capacity;
//函数的定义
void StackPush(Stack* S, int x) {
if (S->top == S->capacity - 1) {
S->capacity = S->capacity == 0 ? 4 : S->capacity * 2;
S->a = (int*)realloc(S->a, sizeof(int) * S->capacity);
}
S->a[++S->top] = x;
}
void StackPop(Stack* S) {
if (S->top < 0) {
return;
}
S->top--;
}
};
其中,C++兼容了C语言的几乎所有用法,同时,将结构体升级为类,类的类名就是类型,如上述代码中Stack就是一整个类型,只需在定义时加上struct,在运用时不需要加struct。如下:
int main() {
//定义stack1和stack2两个类
Stack stack1;
Stack stack2;
return 0;
}
上面结构体的定义,在C++中常用class来代替,效果都是一样的。如将struct Stack{};换成class Stack{},class与struct之间也有不同之处,具体不同下面和以后的文章会详细讲解。
3-2,类的访问权限
类的权限是用于管理类是否可以被外部访问。C++用类将不同对象的属性与方法结合在一块,通过访问权限选择性的将其提供给外部的用户使用。类的权限类型和注意要素如下图:
这里要注意的是class与struct的不同之处,不光是在权限上,在继承和模板参数列表位置,struct和class也有区别,本文只是初步介绍类,在此不作过多讲解,后面的文章将会详细介绍。
权限的运用代码;
struct Stack {
private://以下的变量对象设置私有权限,只能在类中使用
int* a;
int top;
int capacity;
public://以下的函数定义设置为公有权限,可以在类Stack外部使用
void StackPush(Stack* S, int x) {
if (S->top == S->capacity - 1) {
S->capacity = S->capacity == 0 ? 4 : S->capacity * 2;
S->a = (int*)realloc(S->a, sizeof(int) * S->capacity);
}
S->a[++S->top] = x;
}
void StackPop(Stack* S) {
if (S->top < 0) {
return;
}
S->top--;
}
};
3-3,类的使用
1,类中函数的定义
C++的类中,由于函数都自带inline,所以常常将内部大的函数只声明,在外部定义,把内部小的函数直接定义,系统将自动识别为内联函数。当在外部定义类中的函数时,需要运用域的限定符"::",因为类其实是定义了一个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用"::"作用域操作符指明成员属于哪个类域。不光是类,只要运用了"{}"大括号,其实都是一个作用域。样例代码如下:
class Person
{
public:
void Student(char* name1, char* gender1, int age1);
private:
char name[20];
char gender[3];
int age;
};
//这里需要指定Student是属于Person这个类域的函数实现
void Person::Student(char* name1, char* gender1, int age1)
{
strcpy(name, name1);
strcpy(gender,gender1);
age = age1;
//输出Person中的变量,因为此函数就是调用Person类中的函数,使用时可直接认为在类中使用
cout << name << " " << gender << " " << age << endl;
}
2,类中对象的使用
类中对象的使用跟类中函数定义不一样,之前说过,类如同C中结构体一般,当使用类时就相当于使用结构体,因此,用法也自然跟结构体一样,唯一要注意的是使用哪个对象时一定要把对象的权限设为public,即公有的。代码如下:
#include <iostream>
using namespace std;
class Person
{
public:
void Student(char* name1, char* gender1, int age1);
char name[20];
char gender[3];
int age;
};
void Person::Student(char* name1, char* gender1, int age1)
{
strcpy(name, name1);
strcpy(gender,gender1);
age = age1;
cout << name << " " << gender << " " << age << endl;
}
int main() {//定义类a1
Person a1;
strcpy(a1.name, "张三");
strcpy(a1.gender, "45");
a1.age = 15;
a1.Student(a1.name, a1.gender, a1.age);
return 0;
}
3-4,类对象模型
1,类对象的存储方式
类存储的规则是这样的,类中的变量是存储在类中,但类中的函数没有存储在类中,而是存放在专门的类成员函数表中。如图:
2,类的大小
上面我们说过类的存储方式,那么一个类的大小,实际就是该类中”成员变量”之和,要注意的是内存对齐情况和空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。(内存对齐在之前C语言中结构体存储的那章博客上就有详细说明,在这里我们做简单的介绍,若有不明白的可观看本人之前的那章博客)。
内存对齐规则:
1,第一个成员在与结构体偏移量为0的地址处。
2,其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 注意:对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。VS中默认的对齐数为8。
3,结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4,如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
演练代码如下:
#include <iostream>
using namespace std;
class Person
{
public:
void Student(char* name1, char* gender1, int age1);
char name[20];
int age;
char gender[3];
};
class Person1
{};
int main() {
Person a1;
Person1 a2;
cout << sizeof(a1) << endl;//输出28
cout << sizeof(a2) << endl;//输出1
return 0;
}