结构体
自定义的数据类型,C++中定义结构体变量时,可以省略struct。
函数中调用结构体变量时,实参传递给形参时发生了内存拷贝,效率低
//形参用结构体变量
struct student{
//成员变量
char name[10];
int number;
};
void func(student stu){
strcpy(stu.name,"lisi");
stu.number = 2;
}
int main(int argc, char const *argv[])
{
student stu1;
stu1.number = 1;
strcpy(stu1.name,"zhangsan");
cout<<stu1.name<<" "<<stu1.number<<endl;
func(stu1);
cout<<stu1.name<<" "<<stu1.number<<endl;
return 0;
}
//输出为:
//zhangsan 1
//zhangsan 1
可以使用引用&
来进行传递,不会发生内存拷贝。
//形参用结构体引用
//修改func函数为func1
void func1(student &stu){
strcpy(stu.name,"lisi");
stu.number = 2;
}
//使用 func1(stu1); 调用
//输出为
//zhangsan 1
//lisi 2
也可以使用结构体指针进行传递
//形参用结构体指针
//修改func函数为func2
void func2(student *stu){
strcpy(stu->name,"lisi");
stu->number = 2;
}
//使用 func2(&stu1); 调用
//输出为
//zhangsan 1
//lisi 2
c++中的结构体具备了c语言中的结构体的全部功能,还增加了拓展功能
c++中的结构体不仅有成员变量,还可以定义成员函数
public和private成员修饰符
public
:用该修饰符修饰结构体/类中的成员变量/成员函数,就可以被外界访问。
provate
:用该修饰符修饰结构体/类中的成员变量/成员函数,只能被内部定义的成员函数使用。
c++中的结构体和类基本相同,结构体中不加成员修饰符默认为public,类中不加成员修饰符默认为private;结构体继承默认为public,类继承默认为private
类的组织
类的定义代码放在.h
头文件中,头文件名与类名相同;
类的实现放在.cpp
文件中,与类名相同,导入对应的头文件;
函数与后置返回类型
函数定义中,形参如果用不到的话,则可以不给形参变量名字,只给起类型,例如void func(int a, int){}
函数定义时,可以只有形参类型,没有形参名,例如void func(int, int);
函数返回指针问题
int *func(){
int tmp = 9;
return &tmp; //不可以,tmp为临时变量,在执行完func函数后,tmp被系统回收,不能够再使用,可以将tmp设置为全局变量
}
int *p = func();
函数返回引用问题
int &func(){
int tmp = 9;
return tmp;
}
int &p = func();
p = 10; // 不可以,原因与指针相同,tmp地址被回收,p与tmp地址相同。
int q = func();
q =10; //可以
把函数返回类型放在函数名之前,这种写法叫作前置返回类型;在c++11中引入后置返回类型,在函数声明和定义中,将返回类型写在参数列表之后
auto func(int a,int b) -> void; //函数声明
auto func(int a,int b) -> void{
} //函数定义
内联函数
函数定义之前加inline
,这个普通函数就成了内联函数。
一般用在函数体很小,调用频繁的函数,循环,分支,递归尽量不要出现在inline函数中。
在编译阶段对inline
函数进行处理,系统尝试将调用该函数的动作替换为函数本体,来提升性能。
inline
只是开发者对编译器的一个建议,编译器可以尝试去做,也可以不去做,取决于编译器,程序员不能控制。
内联函数的定义要放在头文件中
可能会造成代码膨胀问题;各种编译器处理可能不相同
string
定义和初始化string
string s1; //默认初始化
string s2 = "hello"; //把hello拷贝到s2代表的内存中,不包括"\0"
string s3("hello"); //与s2相同,写法不同
string s4 = s2; //把s2的内容拷贝到s4中,s2与s4内存不同
string s5(6,'a'); //将s5初始化为aaaaaa
string对象上的操作
string s1;
//判断是否为空
s1.empty();
//返回字节/字符数量
s1.size();
s1.length();
//返回s1中的第n个字符
int n = 1;
s1[n];
// s1 + s2 将s1与s2连接
//string的赋值
string s2 = "abcd";
string s3 = "ef";
s3 = s2; // s3为abcd
//返回一个字符串中的内容指针,正规c字符串指针常量,以"\0"结尾
const char *p;
p = s1.c_str();
s1 = "abc" + "cde"; //不能这样加
s1 = "abc"+s2+"def"; //可以
//范围for的应用
for(auto c:s1){
//不能改变s1中的值
}
for(auto &c:s1){
c = toupper(c); //可以改变s1中的值,toupper()将小写变为大写,大写不变。
}
vector
集合或者动态数组,可以把若干对象放入其中,也被成为容器。初始化vector<数据类型> 名称
vector<int> a; //可以
vector<int *> b; //可以
vector<int &> c; //不可以,引用只是别名,指针不是对象
// 初始化
vector<int> a; //默认
vector<int> b(a); //元素拷贝初始化,或者vector<int> b = a;
vector<int> c = {1,2,3,4}; //列表初始化赋值
vector<int> d(15,0); //创建15个int类型,且值为0
a.push_back(1); //向vector末尾加入数据
a.empty(); //判断是否为空
a.size(); //返回元素个数
a.clear(); //移除所有元素,将容器清空
a[n]; //返回a中的第n个元素
//范围for,与string相同
迭代器
迭代器是一种遍历容器内元素的数据类型,有点像指针,可以理解为迭代器用来指向容器中的某个元素。
通过迭代器,我们就可以读容器中的元素值,读string中的每个字符,还可以修改某个迭代器所指向的元素值。
迭代器类型
vector<int> iv = {100,200,300};
//vector<int>::iterator iter; // 定义迭代器
//iter = iv.begin(); // 如果容器内有元素,则begin返回的迭代器指向容器的第一个元素。
//iter = iv.end(); // end返回的迭代器指向的并不是末端元素,而是末端元素的后面,一个不存在的元素
//iter = iv.rbegin(); // 如果容器内有元素,则begin返回的迭代器指向容器的最后一个元素。
//iter = iv.rend(); // rend返回的迭代器指向的并不是第一个元素,而是第一个元素的前面,一个不存在的元素
//如果一个容器为空,那么begin和end返回地迭代器相同
// 迭代器的使用
for(vector<int>::iterator iter = iv.begin(); iter != iv.end();iter++){
cout<<*iter<<endl;
}
// 反向迭代器的使用
for(vector<int>::reverse_iterator riter = iv.rbegin(); riter != iv.rend(); riter++){
cout<<*riter<<endl;
}
迭代器运算符
*iter
:返回迭代器iter所指向元素的引用,必须要保证这个迭代器指向的是有效的容器元素
++iter、iter++
:让迭代器指向容器的下一个元素,已经指向end时不能在++
--iter、iter--
:让迭代器指向容器的上一个元素,已经指向开头元素时不能在–
iter1 == iter2、iter1 != iter2
:判断两个迭代器是否相等
(*iter).num、iter->num
:应用结构中的成员
const_iterator迭代器
const_iterator迭代器表示值不能改变的迭代器,即指向的元素值不可变,而不是迭代器本身不能改变,只能从容器中读出元素,不能改变。
常量容器只能用const_iterator迭代器来进行遍历。
cbegin()
和cend()
返回的都是const_iterator迭代器
迭代器失效
在范围for语句中或操作迭代器的过程中,不能改变容器的容量,不能增加或者删除容器中的元素,否则会使迭代器失效
while(iter != iv.end()){
iter = iv.erase(iter);
} //迭代器回收
while(!iv.empty()){
auto iter = iv.begin();
iv.erase(iter);
}
iv.clear();
评论区