C++入门02
自动推断类型
主要两种自动推断的类型
auto
-
C语言中的auto是一种自动的存储类型,就是系统默认的,在C语言中auto不是很常用,C++做了修改后,auto就在C++中展示了优点
-
使用时一定要初始化,因为auto是根据初始化的值去推断数据的类型
auto j=100; //auto推断出j是整型
double dNum=3.14;
auto k=dNum; //auto推断出k是double类型
auto t; //这样是错误的,auto类型必须初始化
auto a=j+dNum; //这里推断出a是double型,因为int+double->double
decltype
-
定义一个变量与某一表达式的类型相同,但并不想用该表达式初始化这个变量,这时我们需要decltype变量
-
使用前必须要存在至少一个的数据类型,才能使用decltype自动推断
int iNum;
decltype(iNum) decNum=3;
//这里就是decNum的类型和iNum的一样的意思 那么decltype(iNum) 就是一个类型
//decltype(要自动推断的变量或表达式) 新的标识符;
double dNum=3.14;
iNum=9;
decltype(dNum+iNum) result;
//这里就不需要管dNum+iNum的结果是什么类型,直接自动推断完事,反正我想要的只是这个结果的类型
获取自动推断类型的关键字
typeid的使用,只需要知道怎么使用就行
#include<iostream>
using namespace std;
int main()
{
int i = 0;
auto j = i;
cout << typeid(j).name() << endl;
//输出: int
double dNum = 99.8;
auto k = i + dNum;
cout << typeid(k).name() << endl;
//输出: double
decltype(k) val;
cout << typeid(val).name() << endl;
//输出: double
return 0;
}
string类的基本用法
在C语言中的字符串都是用字符数组或者是字符指针表示的字符串,但是这样表示 的字符串不能直接相加(需要strcat),不能直接复制(需要strcpy)等功能,因此,C++就创建了一个string类提供这些功能,然后string类就专门来做字符串的工作。
//C语言定义两个个字符串,然后将这两个字符串连接
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char* str = "ILoveyou";
char* str2 = "IMissyou";
strcat(str, str2);
printf("%s\t%s\n", str, str2);
//运行之后会发现这个是错的,因为str的内存不够装str2
return 0;
}
//改写上面的程序,这个是可以正确输出的
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char str[20] = "ILoveyou";
char str2[10] = "IMissyou";
strcat(str, str2);
printf("%s\t%s\n", str, str2);
//输出: ILoveyouIMissyou IMissyou
return 0;
}
回顾了C语言的代码后开始进入string类的使用,直接当作普通的类型使用。
#include<iostream>
using namespace std;
int main()
{
string str = "ILoveyou";
string str2 = "IMissyou";
str = str + str2; //可以直接相加,也可以直接复制,赋值的时候就存在一个隐式的调用string类中的复制的函数
cout << str << '\t' << str2 << endl;
//输出: ILoveyouIMissyou IMissyou
return 0;
}
相对于C语言的字符数组和字符指针来说string就更加的简单使用,不用考虑溢出什么的。
string str="ILoveyou";
cout << str.length() << endl;
//直接输出字符串的长度:8
//如果是C
char* str2="ILoveyou";
printf("%d\n",strlen(str2)); //8
因为string是一个类,里面还有很多东西,先留个悬念。入门只要掌握string的基本用法就可以了。
C++的结构体和类
在C语言中的结构体可以封装多种基本数据类型,但是不能有函数,在C++中的结构体就添加了结构体中的成员函数的概念。C语言也没有类的概念的,C++中就引出了类的说法,类将是C++的基本单元,后面的面向对象的学习就经常和 类打交道。而C++的结构体和类本质上是一样的,区别仅仅在于默认的权限限定不同,结构体默认的权限是公有性 public,类的默认权限限定是私有性 private
结构体
- struct 结构体名{}; 跟C语言的结构体定义一样
//C语言的结构体
struct Stu
{
char name[10]; //不可以初始化
int age;
int num;
//没有成员函数
};
//C++的结构体
struct Stu
{
string name="C++可以初始化"; //可以初始化
int age=0;
int num; //也可以不初始化
//可以有成员函数
void print()
{
//可以在结构体中直接实现函数
cout<<name<<'\t'<<age<<'\t'<<num<<endl;
}
//也可以在结构体中声明在结构体外实现
void printStu();
//还可以重载
void initData(string name1,int age1)
{
name=name1;
age=age1;
num=1001;
}
//重载initData
void initData(string name1,int age1,int num1)
{
name=name1;
age=age1;
num=num1;
//在结构体中实现的函数叫内联函数
}
};
//结构体外实现的成员函数要加结构体名限定
void Stu::printStu()
{
//name是结构体Stu的name
cout<<name<<endl;
}
结构体定义对象(变量)
其实对象和变量是差不多的概念,在C++和Java等这些面向对象的编程语言中更喜欢把结构体和类定义出来的变量叫做对象。正所谓面向对象就面向变量编程,给变量封装一系列的操作,使用的时候直接通过变量来操作就好,于是就把变量称为对象就比较符合人类的思维。
- C语言中使用结构体定义变量时是采用struct 结构体名的方式定义的
struct Stu stu; //struct Stu 是一种类型
- C++中使用结构体定义变量时,可以保留C的风格,也可以直接使用结构体名的方式定义结构体变量
struct Stu stu; //正确Stu stu2; //也正确
结构体的访问
依然是和C语言一样,结构体变量用 . 访问,结构体指针用->访问
//假设现在要访问上面的结构体的成员
//先用结构体定义出一个对象出来
Stu stu;stu.name="HAX"; //对象访问结构体Stu中的name
stu.age=19; //对象访问结构体Stu中的age
stu.num=2020; //对象访问结构体Stu中的num
stu.print(); //对象访问结构体中的print()函数
//创建一个结构体指针
Stu* pstu=&stu;
pstu->printStu(); //指针访问结构体中的printStu()函数
类
class 类名{};
类的东西和结构体是差不多的,仅仅只是默认限定不同
class MM
{
public: //表示以下的代码是公有的属性
string name;
int age;
int num;
//这里是形参缺省的写法
void initData(string name1="",int age1=0,int num1=0);
void print();}; //类的这个地方的分号也是不能少的
//类外实现的函数需要类名限定,如果类中的成员函数采用缺省的写法,在类外实现的时候不写缺省
void MM::initData(string name1,int age1,int num1)
{
name=name1;
age=age1;
num=num1;
}
//inline 修饰的成员函数表示的是内联函数
inline void MM::print()
{
cout<<name<<'\t'<<age<<'\t'<<num<<endl;
}
类创建对象的时候也是直接使用类名加对象名就可以了
MM mm; //类创建对象MM* pmm=&mm; //类创建指针
访问类中的成员
-
类的对象就直接用 . 的方式访问,类的指针就直接用 -> 的方式访问
MM mm; mm.name="张靓颖"; //对象访问类中名字 mm.age=19; //对象访问类中的年龄 mm.num=2020; //对象访问类中的号码 mm.print(); //对象访问类中的print()函数 MM* pmm=&mm;pmm->print(); //对象访问类中的print()函数
三种权限限定词
-
公有属性:public
表示在public: 之后直到下一个限定词之前的成员都是公有的,类外对象都可以访问得到
-
保护属性:protected
表示在protected: 之后直到下一个限定词之前的成员都是被保护的,类外对象不能访问
-
私有属性:private
表示在private: 之后直到下一个限定词之前的成员都是私有的,类外对象不能访问
#include<iostream>
using namespace std;
class MM
{
public:
void initData(string name1,int age1,int money1)
{
name = name1;
age = age1;
money = money1;
}
void print()
{
cout << name << '\t' << age << '\t' << money << endl;
}
protected:
string name;
int age;
private:
int money;
};
int main()
{
MM mm;
//这些是错的,因为name,age,money都是类外对象访问不到的
//mm.name = "w";
//mm.age = 0;
//mm.money = 0;
//所以提供一个公有接口用来初始化对象
mm.initData("w", 0, 0);
mm.print();
return 0;
}
类中的数据成员一般称为属性,类中的成员函数一般称为行为
一般类中数据成员都是放在protected限定或者是private限定,而成员函数一般是放在public限定
即属性采用保护限定或私有限定,行为采用公有限定
-
类中不写限定词的地方表示默认限定
-
类种的限定词可以不按顺序写,也可以有多个同种的限定词
class MM
{
//这里不写限定词表示默认限定,类的默认限定是私有属性
public: //公有属性
void print();
protected: //保护属性
int age;
public: //再写一个公有属性
void initData();
private: //私有属性
int money;
protected: //在写一个保护属性
int j;
//类中的所有成员随便写,但是毕竟是面向对象,所以标识符尽量是英文单词组合
};
私有属性和保护属性都是类外对象访问不到的,那它们有什么区别呢?在后面的继承和多态再说它们之间的区别。
C++的动态内存申请部分
说到动态内存申请就一定要提到C语言的三个动态内存申请的函数 和一个释放函数,还有一个断言的函数
断言函数和头文件
-
函数:asser
-
头文件:assert.h
-
作用:就是用来判断申请的内存是否成功,成功就assert就什么都不干,失败这会引发中断,不让程序继续走下去
C语言中
三个动态内存申请的函数
- malloc
直接上代码
#include<stdio.h>
#include<stdlib.h> //malloc的头文件
#include<assert.h> //再包含断言的头文件
int main()
{
int length=10;
int *p=(int*)malloc(sizeof(int)*length); //申请了10个int的内存
assert(p); //放一个断言,判断是否申请成功
for(int i=0;i<length;i++)
{
printf("%d ",p[i]); //直接当作数组使用
}
//打印出来的都是垃圾值,因为使用malloc申请的内存是不会初始化的
free(p); //释放申请来的内存
p=NULL;
return 0;
}
- calloc
直接上代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
int length = 10;
int* p = (int*)calloc(length, sizeof(int));
assert(p);
for (int i = 0; i < length; i++)
{
printf("%d ", p[i]);
}
//打印出来的都是0,因为calloc会将申请来的内存都初始化为0
free(p);
p=NULL;
return 0;
}
-
realloc
直接上代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
int length = 10;
int* p = (int*)calloc(length, sizeof(int));
assert(p);
for (int i = 0; i < length; i++)
{
printf("%d ", p[i]);
}
//打印10个0
printf("\n");
int*p2 = (int*)realloc(p, 12);
for (int i = 0; i < length+2; i++)
{
printf("%d ", p2[i]);
}
//打印出来的是垃圾值,因为realloc只是在原来的基础上扩充,并不会初始化
free(p);
p = NULL;
p2 = NULL;
return 0;
}
一个配套的释放函数
-
free
free的使用上面已经看到了,就是用来释放内存里面的东西的
就比如说申请了一个房子,然后在这个房子吃喝拉撒之后呢,用这个free函数来打扫这个房子
C++中
一个动态内存申请关键字:
-
new
之前的C语言都是采用函数的方式申请内存,现在到C++采用的就是关键字new
这个new关键字挺有意思的。我们为什么要学C++,回答为了new一个对象!错了,它不仅能new一个还能new一群对象!
new一个对象
int* p=new int; //这里没有初始化
int* p2=new int(3); //这里申请内存了还给这个内存初始化为3
double* p3=new double(9.9); //这里申请了一个double类型的内存并初始化为9.9
//同样的方法
char* p4=new char;
float* p5=new float;
//不仅能new基本的数据类型还能new复合的数据的数据类型,就像是结构体和类也可以new
//假设存在了一个struct Stu和一个class girlFriend
Stu* p6=new Stu;
girlFriend* p7=new girlFriend;
//好了,这个就是new了一个girlFriend
- new一群对象
int*p=new int[3]; //new了3个int,没有初始化
int*p2=new int[4]{2,5,7,6}; //new了4个int,并且初始化为2,5,7,6
//申请的数组初始化不能用圆括号,只能用花括号
for(int i=0;i<4;i++)
{
cout<<p2[i]<<'\t';
}
//输出: 2 5 7 6
int count;cin>>count;
//new一群girlFriend,想要多少就输入多少给count
girlFriend*p3=new girlFriend[count];
//这就是一群girlFriend了
一个配套的释放关键字:
-
delete
和free一样也是用来释放申请来的内存的,但是原理不同,所以,当采用malloc等函数的方式初始化时就采用free的方式释放
如果采用new的方式就用delete的方式释放,这些都是配套使用的
//直接上代码看
int*p=new int; //申请是new的方式申请,并且只申请一个
delete p; //释放就这样释放
int* p2=new int[3];
delete[] p2; //如果是申请了一段内存的就采用释放数组的方式释放内存
//就只是加了一个中括号就表示释放的是一段内存
girlFriend* p3=new girlFriend[6];
delete[] p3;