字符数组
//单个字符赋值给字符数组,字符大小等于赋值个数,也就是5
char a[] = {'h', 'e', 'l', 'l', 'o'};//不包含'\0'
//"hello"字符串包含了字符串结束标记'\0',所以字符数组下标必须大于等于6
char b[6] = "hello";//{"hello"}---大括号可省
//包含了'\0'后,b和c才完全等价
char c[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
cout << a << endl;
cout << b << "," << c << endl;
程序输出:
hello鄈
hello,hello因为输出字符串只有遇到’\0’才结束,a在内存中的‘\0’位置不确定,所以会输出多余的乱码
- 字符数组长度大于赋值字符串时,余下的字符都赋值‘\0’
- 输出函数输出字符串只要遇到第一个‘\0’就结束
char c[10] = "china";
cout << c << endl;//china
c[2] = '\0';
cout << c << endl;//ch
不管是scanf还是cin输入函数,都把空格作为结束标识,也就是字符串不能包含空格
char s1[10], s2[10], s3[10]; // scanf("%s%s%s", s1, s2, s3); cin >> s1 >> s2 >> s3; cout << s1<<"," << s2<<"," << s3 << endl;
- puts函数,只能输出一个字符串,而且输出自动换行
char s1[] = "hello world"; char s2[] = "are you ok!"; puts(s1); puts(s2); /*输出: hello world are you ok!*/
- gets函数,只能输入一个字符串,而且包含空格
char s1[100]; gets(s1);//可以输入一个包含空格的字符串 puts(s1); /*输出: 1 2 hello word are you 1 2 hello word are you*/
strcat(a,b)
- 字符串连接函数,把字符数组b中的内容连接到字符数组a中
- 也就是字符数组a从’\0’开始,把字符数组b的字符逐个拷贝
strcpy(字符数组a,字符串b)
- 把b的内容拷贝到a中,b的内容会覆盖a的对应内容,\0也会被拷贝
- 假设b的长度比a长,那么由于\0提前出现,后面b多出的字符不被打印
strcmp(字符串1,字符串2 )
从左到右逐个按字典排序进行字符串的比较
- 字符串1==字符串2,返回0
- 字符串1>字符串2,返回正数;字符串1<字符串2,返回负数
strlen(字符串)
字符串的实际长度,也就是不包含\0的长度。也就是字符串从开始到第一个\0的长度
char s1[100] = "helloworld"; s1[6] = '\0'; cout << strlen(s1);//6
例子:
#include <string.h>
int main()
{
char s1[100] = "helloworld";
char s2[100] = "world";
strcpy(s1, "h a b n m");
strcat(s2, "hellli");
cout << s1 << ";" << s2 << endl;
cout << strlen(s1)<<"," << strlen(s2) << endl;
cout << strcmp(s1, s2) << endl;
return 0;
}
/*
h a b n m;worldhellli
9,11
-1
*/
注意字符数组名和字符串地址和字符串第一个元素地址相同
char str[100] = "helloworld";
printf("%d\n", str);
printf("%d\n", &str);
printf("%d\n", &str[0]);
/*
-1394606896
-1394606896
-1394606896
*/
char s1[100] = "helloworld";
char s2[100] = "helloworld";
if(s1!=s2){//这样直接用名字,其实比较的是地址
cout << "not equal";
}
//not equal
字符指针
char *p1 = “I love China”;
char *p2 = “I love China”;
p1和p2都指向了字符串常量
- p1和p2的地址相同,这是因为字符串常量在内存中有单独的存储区域,所以p1和p2可以访问不能修改
- p1和p2可以指向其他的字符串,只是不能修改指向字符串的内容
把字符串逐个赋值
char a[10] = "hello!";
char b[10];
char *p1 = a;
char *p2 = b;
//把a的内容赋值给b
for (; *p1 != '\0';p1++,p2++){
*p2 = *p1;
}
*p2 = '\0';
//函数
void copystr(char from[],char to[]){//char *from,char *to
int i = 0;
while(from[i]!='\0'){
to[i]=from[i];
i++;
}
to[i] = '\0';
}
// char a[] = "source string";
// char b[100];
void copystr(char *from,char *to){
int i = 0;
while(*from){
*to++ = *from++;
}
to[i] = '\0';
}
void copystr(char *from,char *to){
int i = 0;
while(from[i]!='\0'){
*(to+i)=*(from+i);//一般不用
i++;
}
*(to+i) = '\0';
}
注意:
char a[100]; //a="what a lovely dog";//出错,a是地址 strcpy(a, "i love dog"); const char *b; b = "i love dog";//可以,指针指向了字符串常量内存 b = b + 5;//指针值可以改变
字符数组不可以直接”=”赋值
字符指针可以,但字符指针不可以改变指向的字符串内容
string 类
编译器对所有类都有默认的:拷贝构造和拷贝赋值
带有指针成员的类对象(class with pointer members),必须要有:
copy CTOR
—拷贝构造copy op=
—拷贝赋值
但是如果
类的成员
有指针,需要自己写,因为编译器会让不同对象的指针指向同一个内存
我们希望的是拷贝字符串内容,然后不同类的指针指向各自的内存
浅拷贝
:
不同对象指针指向同样的内存,如果其中一个对象改变了内存值,其他对象也会改变
深拷贝
:
不同对象拷贝了内存的内容后,各自指针指向自己拷贝的内容,一个对象的内容改变不影响其他对象
写出自己的mystring类
私有成员是字符指针:char* cstr
函数内需要实现:
- 构造函数:mystring(const char* cdata=0)
带有初始值0,即’\0’
- 拷贝构造函数:mystring(const mystring& str)
既然是构造,说明私有成员cstr没有分配过内存
参数必须是这个类成员的引用
- 拷贝赋值函数: mystring& operator=(const mystring& str)
注意该函数返回值是类成员
由于是赋值,所以=的左边内存可能已经分配
首先判断是不是自我赋值
然后重新分配内存,并拷贝str内容
- 析构函数:~mystring()
这里很简单,用delete[] 释放cstr的内容
- 最后用一个函数返回私用成员的指针: char* getcstr() const
类定义
class mystring{
public:
//构造函数,带有默认参数0
mystring(const char *cdata=0);
//拷贝构造函数:没有返回值,而且参数是本类对象的引用
mystring(const mystring& str);
//拷贝赋值函数:重载运算符=,返回值类引用
mystring& operator=(const mystring& str);
//返回类私用成员函数
char* getCstr() const{
return cstr;
}
//析构函数
~mystring(){
delete[] cstr;
}
private:
char *cstr;
};
函数实现
//构造函数
/*注意带默认参数的,类内和类外只能写一次*/
mystring::mystring(const char* cdata){
if(cdata==0){
//cstr为'\0'
cstr = new char[1];
*cstr = '\0';
}
else{
//用到字符数组的strlen和strcpy函数
cstr = new char[strlen(cdata) + 1];
strcpy(cstr, cdata);
}
}
//拷贝构造函数
//既然是构造,说明cstr没有分配内存过
mystring::mystring(const mystring& str){
cstr = new char[strlen(str.cstr) + 1];
strcpy(cstr, str.cstr);
}
//拷贝赋值函数
//注意返回值和传入参数
mystring& mystring::operator=(const mystring& str){
if(this==&str){//判断指针指向
return *this;//什么也不做
}
//重新分配内存
cstr = new char[strlen(str.cstr) + 1];
strcpy(cstr, str.cstr);
//别忘了return
return *this;
}
函数测试
#include"mystring.h"
#include<iostream>
using namespace std;
//为了能用<<输出mystring成员,重载<<
ostream& operator<<(ostream& os, mystring& str){
//<<需要两个参数,一个是os一个想输出的
os << str.getCstr();
return os;
}
int main(){
mystring s1("helloWorld");
mystring s2 = s1;
mystring s3(s1);
mystring s4 = s2;
mystring s5(s3);
cout << s1 << "," << s2 << "," << s3 << endl;
cout << s4 <<"," << s5 << endl;
return 0;
}
注意重载<<,返回值是ostream类成员,在<<左边。
lambda表达式
lambda 表达式是 C++11 中引入的一个非常重要的新特性
lambda表达式定义了一个匿名函数,并且可以捕获到一定范围内的变量
auto f = [](int a = 8) -> int{
return a + 1; };
cout << f() << " " << f(11) << endl;
lambda表达式的形式
[捕获列表](参数列表)->返回值类型{ 函数体};
- lambda表达式是一个匿名函数,没有函数名,而且可以在函数内部定义
int test(int a){
//函数内部定义lambda匿名函数
auto m = [](int a) -> int
{ return a * 2; };
return a + m(a);
}
-
lambda的返回类型是后置的,函数没有返回值不写,而且返回值类型可以在容易被识别情况下省略
没有参数也可以没有参数列表,()可写可不写,参数也可以带有默认值
编译器如何推断不出来返回类型会报错,需要显式写出
//没有返回值和参数列表
auto f = [] { cout << "hello,world"<<endl; };
f();//必须加()来访问
//参数带默认值,没有写返回值
auto v = [](int a = 8, int b = 9) { return a + b; };
cout << v(100, 200) << endl;
- 无参时候()可省,无返回类型时->不要写
- []{}; 捕获列表和函数体这两个东西不可省
lambda的捕获列表
如果捕获列表为空,即[],那么lambda函数体内不认识局部变量,但可以认识静态变量
int main(){
int a = 10;
int b = 20;
//报错,lambda函数体内不认识局部变量a,b
auto m = []() -> int{ return a + b; };
return 0;
}
static int b = 20;
int main(){
static int a = 10;//静态变量
//static变量可以被lambda函数体识别
auto m = []{ return a*b; };
cout << m();
return 0;
}
静态变量不可以在lambda表达式下面
auto m = []{ return c; };//报错
static int c = 10;//静态变量不可以在lambda下面
捕获列表的几种类型
//[&] 捕获外部作用域所有的变量,并且以引用的形式
int a = 100;
int b = 200;
int c = 300;
auto f = [&](int d) { a=a+d;
b=b+d;
c=c+d;
return a+b+c+d; };
cout << f(1) << endl;
cout << a << " " << b << " " << c << endl;
/*运行结果:
604
101 201 301
*/
lambda内用&使用变量时一定要注意变量的作用域范围,避免造成意外结果
lambda按值捕获[=]不允许给局部变量进行赋值操作
//[=] 捕获外部作用域所有的变量,并且以值的形式
//所以不允许赋值操作了
int a = 100;
int b = 200;
//注意[=]
auto f = [=](int d) { a=a+d;//报错,因为a不允许修改
b=b+d;//报错
return a+b; };
[this]
一般用于类中,捕获this指针,让lambda和当前成员函数具备相同的访问权限
[=]和[&]都默认了已经使用了this,使用this一般就是为了“lambda使用当前类的函数和变量”
class demo{
public:
demo(int a=10,int b=20):a(a),b(b){}
void myfun(){
auto myLambda = [this] { // 必须加this
return a + b;
};
cout << myLambda()<< endl;
}
private:
int a, b;
};
//demo().myfun()
class demo{
public:
int a = 100, b = 200;
void myfun(int x,int y){
auto myLambda = [=] { //想要访问x,y加上[=]
//而且x和y不可以在左边
a = x;
b = y;
return a + b + x + y;
};
cout << myLambda()<< endl;
}
};
int main(){
demo().myfun(10, 20);//60
cout << demo().a << "," << demo().b << endl;//100 ,200
return 0;
}
[变量名],多个变量名用逗号分割,而且lambda只按值捕获
这些变量,减少资源消耗(不捕获其他变量)
[=,&val1,&val2,&val3]
-------除了val用&,其他都用值,=必须在开头,后面接的变量是&
[&,变量名]
----变量名是按值,必须与&相反
- [&,val1,val2,&val3]报错,val3不可以用&,因为全都默认&了
捕获列表的第一个位置是默认捕获方式
[this,&x,y]{};
class demo{
public:
int a = 100, b = 200;
void myfun(int x,int y){
auto myLambda = [this,&x,y] {
//有了this就可以访问类中变量a,b
//x引用访问,可修改值
x = 1;
return a + b + x + y;
};
cout << myLambda()<< endl;
}
};
注意lambda按值捕获,值是定义之前的
按值捕获
的外部变量,其值都是在lambda中之前的值
char c = 'a';
int x = 100;
auto m = [=]() -> string{//有返回值,()不可省
return c + to_string(x);};
cout << m() << endl;//c100
这里是5而不是10,因为5被赋值了
改成&后,值是修改后的
lambda中的mutable
mutable"易变的"
在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
int x = 5;
//mutable修饰后,需要加上()
auto m = [=]() mutable{
x = 6;//=不可修改x,但是mutable修饰符可以修改
return x;
};
cout << m() << endl;
lambda表达式的类型
c++11中,lambda表达式的类型被称为:闭包类型
(Closure Type)
- 闭包:函数内的函数,又叫可调用对象
- 也就是
auto f=[]()->返回值{};
中的f是:lambda运行时期创建的对象f,auto类型是闭包 - 也就是:auto f=[]{};中f就是一个
未命名的类的对象
lambda表达式是一种特殊的,匿名的,闭包类的对象
可以认为是一个带有operator()的类类型对象,也就是仿函数
可以用std::function和std::bind来保存和调用lambda表达式
#include<iostream>
#include<functional>//std::function和std::bind
#include<string>
using namespace std;
int main(){
/*std::function*/
int y = 100;
function<int(int)> f1 = [=](int x)
{ return x + y; };
std::function<string(string)> f2 = [&](string s) {
return to_string(f1(200)) + s;};
cout << f2("hello") << endl;
/*std::bind*/
//bind(第一个参数是可调用对象,第二个参数是真的函数参数)
std::function<char(char)> f3 = std::bind(
[](char x){ return x; },//第一个参数:lambda可调用对象
'c'//第二个参数,可调用函数的参数
); // bind
cout << f3('c') << endl;//结果是绑定的'a','c'无用但是得写
return 0;
}
捕获列表为空的lambda表达式,可以转换一个普通的函数指针
int main(){
//函数传参:返回值 (*指针变量名)(函数返回值)
int (*fp)(int) = [](int x){ return x * 2; };//[]为空
cout << fp(100) << endl;
string (*func)(int, int) = [](int a, int b){
return to_string(a) + to_string(b) + to_string(a);};
cout << func(11, 22) << endl;
//利用别名----函数返回类型(* 别名)(函数返回值)
using funType = char (*)(char);
funType name = [](char c){ return c; };
cout << name('z') << endl;
return 0;
}
- 函数可以传参:
函数返回值 (*指针名)(函数参数类型)
- 之所以要求捕获列表[]为空,是因为如果是this的类对象,是不能用函数传参的
语法糖就是一种便捷写法的意思
- typedef和using包括lambda表达式都属于语法糖
a[i]就是*(a+i)语法糖,尤其在二位数组更明显
for_each和find-if
for_each在头文件:#include<algorithm>
中
std::for_each 相较于for而言,它可以帮助在STL容器中的每个元素都执行一次fun()函数。
template<typename InputIterator, typename Function>
Function for_each(InputIterator beg, InputIterator end, Function f) {
while(beg != end)
f(*beg++);
}
for_each(iter1,iter2,fun):遍历容器,从iter1到iter2,然后每个迭代器都执行一次函数fun(iteri)
void fun(pair<int,int> x){
cout << x.first + x.second << " ";
}
int main(){
vector<pair<int, int>> mm;
mm.push_back(make_pair(100, 200));
mm.push_back(make_pair(200, 300));
mm.push_back(make_pair(300, 400));
for_each(mm.begin(), mm.end(), fun);
return 0;
}
vector<pair<int, int>> mm;
mm.push_back(make_pair(100, 200));
mm.push_back(make_pair(200, 300));
mm.push_back(make_pair(300, 400));
auto aa = mm.begin();
for_each(mm.begin(), mm.end(), [](pair<int, int> t)
{ cout << t.first << "," << t.second << endl; });
vector<pair<int, int>> mm;
mm.push_back(make_pair(100, 200));
mm.push_back(make_pair(200, 300));
mm.push_back(make_pair(300, 400));
auto aa = mm.begin();
for_each(mm.begin(), mm.end(), [](pair<int, int> t)
{ cout << t.first << "," << t.second << endl; });
for_each求容器的和
vector<int> mm={1,2,3,4,5};
int sum = 0;
for_each(mm.begin(), mm.end(), [&sum](int vec)
{ sum += vec; });
cout << sum << endl;
find_if(iter1,iter2,fun)
find_if算法 :
- 遍历迭代器区间[first, last)上的每一个元素
- 如果迭代器iter满足pred(*iter) == true,表示找到元素并返回迭代器值iter;未找到元素,则返回last。
- 返回值是第一个满足条件的迭代器,不满足,返回end()
也就是为真是停止,假时继续往下遍历
vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
//find_if返回值是迭代器
auto result = find_if(vec.begin(), vec.end(), [](int val){
if(val>7)
return true;
else{
cout << val << " ";
return false;
}
});