复合类型
复合类型是指在其他类型的基础上定义的类型。C++语言有内置的复合类型,包括指针、引用、数组、结构体、共用体和枚举。
一、指针
程序运行时,代码和需要的数据都被存储在内存中,指针持有一个对象的地址,称为指针“指向”该对象。
读取数据时有两种方法,一种直接种间接,当间接访问数据时就用到了指针。
每一个指针都有类型,与指向的数据一致。
指针里存储的是数据的地址,如果要获取这个地址,就要用到取地址运算符 &
例如
int ival = 120;
int *pi = &ival;
char ch = 'a',*pc = &ch;
如果指针指向一个对象,则可以通过指针间接访该对象,使用指针解引用运算符“*”
例如 int x = 100, y = 20;
int *pi = &x;
*pi = y;
空指针:指针值为零的指针
定义空指针的方法(3种)
int *pi=NULL;
int *p1 = nullptr;
int *p2 = 0;
注意事项:
“同类型的指针”可以进行相等(==)或不相等(!=)的比较操作,比较的结果是布尔类型。不是同类型的无法比较。
**通用指针
Void*指针:可以持有任何类型的地址值,这样也会有一个缺点就是,你自己也不谁知道数据的类型。同时它也不允许void指针到其他类型指针的直接赋值。
- 动态存储空间
new与delate运算
new运算符:在堆上动态分配空间,创建对象,并返回对象的地址。
delete运算符:释放堆上的内存。
1、new 类型 或者 new 类型(初始值)
在堆上分配特定类型的单个对象,并返回其地址 例如:int* ip1 = new int;
释放new分配的单个对象的delete形式 delete ip1;
2、new 类型[数组大小]
在堆上分配指定类型和大小的数组(连续内存空间),返回数组首地址
例如:int* ipa = new int[100];
释放new分配的数组 delete[] 指针; delete[ ] pa;
// 被释放内存后的指针叫做空悬指针;
- 左值引用
进行初始化,引用必须被初始化,初始值是一个有内存地址的对象,但是左值引用的初始化和一般变量的初始化不同,一般在初始化变量时,初始值会被复制到新建的对象中。引用在初始化时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。
- 右值引用
形式 类型&&右值引用变量=右值表达式;
右值引用与左值引用的区别
int i=42;
int &r=i;
int &&r2=i*5;
①左值可以寻址,而右值不可以。
②左值可以被赋值,右值不可以被赋值,可以用来给左值赋值。
int &&rr1 = 10;
int &&rr2 = rr1;//错误
int &&rr2 = std::move(rr1) ;//正确。该函数是告诉编译器希望像对待右值那样处理一个左值。
五、CV限定词
const与volatile
const限定词将一个对象限定为常量。也可以限定指针与引用。
另外 c++允许一个非const地址赋值给const指针。
例如:
const int*pi = &ival;
ival=500;//ival没有被限定
*pi=500//不可以,因为pi被限定了
也可以定义指向非const对象的指针常量。 volatile用法与const相同。
六、标准库函数string
使用时要使用<string>头文件库函数
1.字符的读入一般有两种一种是平常的用输入操作符“>>"读入字符串。在输入操作符返回输入流对象时,如果输入流对象处于有效状态,表示没有遇到文件结束或者非法输入,可以用循环来读取未知量的string对象。第二种是使用getline()函数。getline函数可以在读取字符串中保留输入时的空白符。函数从指定的输入流中读取内容,直到遇到换行符为止;
2.几种对string的操作
一.判断string是否为空
使用的函数是empty(),返回一个布尔值。
二.获取string的长度
使用size(),返回字符串的长度,即string对象中字符的个数。
3.比较string对象
对string对象的字符进行比较:
如果两个string对象的长度相同,则两者相等;
4.string对象的赋值和连接
允许把一个string对象的值赋给另一个string对象两个字符串的连接可以直接使用运算符“+”,结果得到一个新的 string对象。复合赋值运算符“+=”则将右操作数的内容追加到左操作数的后面。
例如
string s1 = "hello, ", s2 = "world! " ;
string s3 = s1 + s2;
s1 += s2;
string s4 = s1 + "\n"; //正确
string s6 = s1 + " world" + "\n"; //正确
string s5 = "hello" + "," + s2; //错误
七、结构体struct
结构体把一组来自不同类型的数据组合在一起构成复合类型,其中的每个数据都是结构体的成员。
结构体由关键字struct定义,语法形式:
struct 结构体类型名
{
成员声明;
};
例如
struct X
{
char c;
int i;
float f;
double d;
};
结构体的成员不能独立使用,必须由结构体类型的变量通过成员选择运算符“.”来选择,或者由结构体类型的指针通过“->”运算符选择。
八、标准库函数vector
使用时要使用<vector>头文件
格式 vector<元素类型> 变量名;
初始化时注意事项
- 注意各种括号
vector<int> v1(10);//v1有10个int元素,每个都初始化为0
vector<int> v2{10}; //v2有1个元素,值是10
vector<int> v3[10]; //v3是有10个元素的数组
//每个元素都是一个空vector对象
vector<int> v4(10, 1); //v4有10个int元素,每个都初始化为1
vector<int> v5{10, 1}; //v5有2个int元素,值分别是10和1
- 添加和删除元素
push_back()
将一个值添加到vector的末尾,并使vector的大小增加
pop_back()
删除vector对象末尾的元素(要先检测对象是否为空)
//不能用下标运算符向vector中添加元素
如果要用循环向vector对象中添加元素,不能使用范围for,范围for不能改变所遍历的容器的大小。这时就要使用迭代器
库函数begin()和end()
让指针在数组上的使用更简单更安全
在头文件<iterator>中定义
用法
begin(数组名)
返回指向数组第一个元素的指针
end(数组名)
返回指向数组最后一个元素的下一个位置的指针
例如:利用迭代器将s中的第一个词改成大写形式
string s = "Hello, World!";
auto it = s.begin();
while (it != s.end() && !isspace(*it))
{
*it = toupper(*it);
++it;
}
cout << s << endl;
- 函数 系统库函数和自定义函数
- 自定义函数要定义函数、声明函数、调用函数
定义例如:一个求阶乘的函数定义
函数名fact,返回类型int,一个int类型的形参n
int fact(int n)
{ //函数体开始
int ret = 1;
while ( n > 1 ) //求阶乘
ret *= n--;
return ret; //返回结果
} //函数体结束
声明:返回类型 函数名(参数列表) ;
调用:函数名(实参列表); 调用时不用写参数类型
返回遇到return语句时,结束函数的执行,返回return语句中的值。
- 函数值传递
三种方式 传值 传地址 传引用
一、函数传递是实参的值,是单向传递过程。形参值的改变对实参不起作用。
二、引用传递:把引用作为形参,在函数调用时发生的参数传递,成为引用传递。
通过引用名与通过被引用的变量名访问变量的效果是一样的。
- 传地址:传递参数的地址与传引用相似。
- 函数值返回
返回类型
非void的函数必须返回一个与声明类型匹配的值,否则会引起编译错误
默认情况下,函数的返回值是按值传递的,得到控制权的函数将接收return语句中指定的表达式值的副本。返回值的方式和初始化变量或形参的方式完全一样
- 函数重载
多个函数可以共享同一个函数名,针对不同的参数类型提供不同的操作
例如:
void print(const int *b, const int *e){...}
void print(const int ia[], size_t size){...}
void print(const char *cp){...}
调用:
调用函数时,如果存在多个重载函数,编译器将根据函数调用中指定的实参进行选择