字符数组+自定义string类+lambda+for_each//find_if

字符数组

    //单个字符赋值给字符数组,字符大小等于赋值个数,也就是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;
        }
    });
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值