文章目录
1.auto关键字
1.1auto介绍
C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
#include<iostream>
using namespace std;
int test()
{
return 1;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = test();
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
return 0;
}
注意:因为auto是根据赋值对象推断,所以auto修饰的变量必须要初始化。在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”, 在编译阶段,编译器会对auto替换为推断出的类型。
1.2auto使用规则
auto与指针和引用结合使用。
**用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加& **
int main()
{
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;
cout << typeid(a).name() << endl;
cout << typeid(b).name()<< endl;
cout << typeid(c).name() << endl;
return 0;
}
同一行定义多个变量
auto a=10,b=20; //auto先根据a的类型推断出为int类型,而b=20也是int类型所以可以编译通过。
auto c=10,d=4.0 //编译无法通过;因为auto在编译过程是被替换
1.3auto不能推导的情况
1.auto不能作为函数的参数
void test(auto a)
{
}
2.auto不能作为函数的返回类型
auto test(int a)
{
return 10;
}
3.auto不能直接用来声明数组
void test()
{
int a[]={1,2,3};
auto b[]={4,5,6}
}
1.4范围for循环
//一般的写法
void test1()
{
int array[]={1,2,3,4,5,6,7,8,9};
int len=sizeof(array)/sizeof(array[0]);
for(int i=0;i<len;i++)
{
array[i]*=2;
}
for(int i=0;i<len;i++)
{
cout<<array[i]<<" ";
}
cout<<endl;
}
范围for循环
void test1()
{
int array[]={1,2,3,4,5,6,7,8,9};
for(auto a:array)
{
a*=2;
}
for(auto a:array)
{
cout<<a<<" ";
}
cout<<endl;
}
这里的a只是array[i]的临时拷贝。因此改变a无法改变array的值;
如果想要对array的值进行修改,只需要把a修改为引用。
void test1()
{
int array[]={1,2,3,4,5,6,7,8,9};
for(auto& a:array)
{
a*=2;
}
for(auto a:array)
{
cout<<a<<" ";
}
cout<<endl;
}
范围for循环的使用条件:for循环迭代的范围必须是确定的
对于数组来说,范围是数组第一个元素和最后一个元素的范围。而对于可迭代的类而言,begin和end就是for循环迭代的范围
void testfor(int array[])
{
for(auto& a:array)
cout<<a<<endl;
}
//编译无法通过
2.指针空值nullptr
nullptr是c++11新增加的关键字
头文件中NULL的定义
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
从上面的文件可以看出,在CPP中NULL就是字面常量0;
在C语言中,NULL被定义为无类型指针(void*)的常量
void f(int)
{
cout <<"f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);
f((int*)0);
f((int*)NULL);
return 0;
}
总结:在c++中,指针的初始化尽量使用nullptr
3.类与对象
C++与C语言不同,C语言是面向过程的语言,关注的是过程,分析出求解问题的步骤
C++是基于面向对象语言,关注的是对象,将一件事情拆为不同的对象,靠对象之间的交互完成
3.1类
C语言中,结构体只能够定义变量,C++中结构体不仅可以定义变量还能够定义函数
struct MyStruct
{
void init(const char*name,const int age,const char*sex,const int id)
{
_age = age;
strcpy(_name, name);
strcpy(_sex, sex);
_id = id;
}
void print()
{
cout << _name << " " << _age << " " << _sex << " " << " _id" << endl;
}
char _name[20];
int _age;
char _sex[10];
int _id;
};
int main()
{
MyStruct s;
s.init("张三", 18, "男", 11111);
s.print();
return 0;
}
3.2类的定义
C++中常常使用关键字class定义类;为了兼容C语言,也可以使用struct定义类;但是两者在细节上有些不同,后面会进行介绍。
class student
{
public:
void init(const char*name,const int age,const char*sex,const int id)
{
_age = age;
strcpy(_name, name);
strcpy(_sex, sex);
_id = id;
}
void print()
{
cout << _name << " " << _age << " " << _sex << " " << " _id" << endl;
}
public:
char*_name;
int _age;
char*_sex;
int _id;
};
int main()
{
student s;
s.init("张三", 18, "男", 11111);
s.print();
return 0;
}
3.3类的访问限定符和封装
访问限定符:public(公有),protected(保护),private(私有)
访问限定符的权限是针对类内部和类外部来区分的
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限**作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- class的默认访问权限为private,struct为public(因为struct要兼容C)
class student
{
public:
void init(const char*name,const int age,const char*sex,const int id)
{
_age = age;
strcpy(_name, name);
strcpy(_sex, sex);
_id = id;
}
void print()
{
cout << _name << " " << _age << " " << _sex << " " << " _id" << endl;
}
private:
char*_name;
int _age;
char*_sex;
int _id;
};
int main()
{
student s;
s.init("张三", 18, "男", 11111);
s.print();
cout<<s._name<<endl;
return 0;
}
//编译报错:
//student::_name”: 无法访问 private 成员(在“student”类中声明)
3.4封装
面向对象有三大特征:封装,继承,多态
封装:本质上是一种管理数据和操作数据的一种思想。将数据和操作数据的方法有机结合,隐藏对象的属性和实现的细节,仅对外公开接口与对象进行交互。
3.4类的作用域
定义一个类就定义了一个新的作用域,因此需要在类外使用成员变量时,需要指明成员属于的类域。
class student
{
public:
int age;
int id;
};
class teacher
{
public:
int age;
int id;
};
3.5类对象模型
class tmp
{
public:
void init(int age, char* name, int id)
{
_age= age;
strcpy(_name, name);
_id = id;
}
void print()
{
cout << _age << " " << _name << " " << _id << endl;
}
private:
int _age;
char _name[20];
int _id;
};
tmp类型实例化后大小是多少?
int main()
{
tmp t;
t.init(18,"张三",1111);
cout << sizeof(t) << endl;
return 0;
}
输出结果为28
struct tmp
{
int _age;
char _name[20];
int _id;
};
int main()
{
tmp t = { 18,"张三",11111 };
cout << sizeof(t) << endl;
return 0;
}
说明成员函数并没有在对象中占用空间,因此可以推断出类对象的存储方式。
从汇编代码验证
class tmp
{
public:
void init(int age, const char* name, int id)
{
_age= age;
strcpy(_name, name);
_id = id;
}
void print()
{
cout << _age << " " << _name << " " << _id << endl;
}
private:
int _age;
char _name[20];
int _id;
};
int main()
{
tmp t;
t.init(18,"张三",1111);
t.init(19, "李四", 2222);
return 0;
}
两个对象调用的是同一地址的函数
3.6类的大小和内存对齐
class A1
{
public:
void f1() {}
private:
int _a;
};
// 类中仅有成员函数
class A2
{
public:
void f2() {}
};
// 类中什么都没有---空类
class A3
{};
int main()
{
cout << sizeof(A1) << " " << sizeof(A2) << " " << sizeof(A3) << endl;
return 0;
}
**结论:**当类对象只有成员函数据或者什么也没有时,类对象的大小为1字节
内存对齐的规则
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到对齐数的整数倍的地址处。
- 注意:对齐数 =编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8
- 结构体总大小为:最大对齐数(所有变量类型对齐数最大者与默认对齐参数取小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。