C++ 基础入门(二)类和对象(class和object)

C++ 基础入门(二)类和对象(class和object)

2.1 C++ 类的定义和对象的创建

2.1.1 C++ 类的定义和对象的创建详解

类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量;创建对象的过程也叫做类的实例化。每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数。

有时又将类的成员变量称为类的属性(Property),将类的成员函数称为类的方法(Method)。在面向对象的编程语言中,经常把函数(Function)称为方法(Methon)。

与结构体一样,类只是一种复杂的数据类型声明,不占用内存空间。而对象是类这种数据类型的一个变量,或者说是通过类这种数据类型创建出来的一份实实在在的数据,所以占用内存空间。

2.1.2 类的定义

类是用户自定义的类型,如果程序中要用到类,必须提前说明,或者使用已存在的类,C++ 语法本身并不提供现成的类的名称、结构和内容。

一个简单的类的定义:

1. class Student {
2. pubilc:
3. 		char *name;
4. 		int age;
5. 		float score;
6. 		
7. 		// 成员函数
8. 		void say() {}
9.  };

class 是 C++ 中新增的关键字,专门用来定义类。 Student 是类的名称;类名的首字母一般大写,以和其他的标识符区分开。{} 内部是类所包含的成员变量和成员函数,它们统称为类的成员(Member);由 {} 包围起来的部分有时也称为类体,和函数体的概念类似。public 也是 C++ 的新增关键字,它只能用在类的定义中,表示类的成员变量或成员函数具有“公开”的访问权限。

注意:在类定义的最后有一个分号 ; ,它是类定义的一部分,表示类定义结束了,不能省略。

类只是一个模板(Template),编译后不占用内存空间。所以在定义类时不能对成员变量进行初始化,因为没有地方存储数据。只有在创建对象以后才会给成员变量分配内存,这个时候就可以赋值了。

2.1.3 创建对象

在创建对象时,class 关键字可要可不要,但是出于习惯我们通常会省略掉,例如:

1. class Student LiLei;// 正确
2. Student LiLei;// 正确

处理创建单个对象,还可以创建对象数组。

2.1.4 访问类的成员


类通常定义在函数外面,当然也可以定义在函数内部,不过很少这样使用。

2.1.5 使用对象指针

下面代码中创建的对象 stu 在栈上分配内存,需要使用 & 获取它们的地址:

1. Student stu;
2. Student *pStu = &stu;

pStu 是一个指针,它指向 Student 类型的数据,也就是通过 Student 创建出来的对象。

当然你也可以在堆上创建对象,例如:

01. Student *pStu = new Student;

在栈上创建出来的对象都有一个名字,比如 stu,使用指针指向它不是必须的。但是通过 new 创建出来的对象就不一样了,它在堆上分配内存,没有名字,只能得到一个指向它的指针,所以必须使用一个指针变量来接收这个指针,否则以后再也无法找到这个对象了,更没有办法使用它。也就是说,使用 new 在堆上创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。

栈内存是程序自动管理的,不能使用 delete 删除在栈上创建的对象;堆内存由程序员管理,对象使用完毕后可以通过 delete 删除。在实际开发中,new 和 delete 往往成对出现,以保证及时删除不再使用的对象,防止无用内存堆积。

有了对象指针后,可以通过箭头 -> 来访问对象的成员变量和成员函数:

2.2 C++ 类的成员变量和成员函数详解

类可以看做是一种数据类型,它类似于普通的数据类型,但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的集合。

类的成员变量和普通变量一样,也有数据类型和名称,占用固定长度的内存。但是,在定义类的时候不能对成员变量赋值,因为类只是一种数据类型或者说是一种模板,本身不占用内存空间,而变量的值则需要内存来存储。

类的成员函数也和普通函数一样,都有返回值和参数列表,它与一般函数的区别是:成员函数是一个类的成员,出现在类体中,它的作用范围由类来决定;而普通函数是独立的,作用范围是全局的,或位于某个命名空间内。

在类体只中直接定义函数时,不需要在函数名前面加上类名,因为函数属于哪一个类是不言而喻的。

但当成员函数定义在类外时,就必须在函数名前面加上类名予以限定。:: 被称为域解析符(也称作用域运算符或作用域限定符),用来连接类名和函数名,指明当前函数属于哪个类。

成员函数必须先在类体中作原型声明,然后在类外定义,也就是说类体的位置应该在函数定义之前。

2.2.1 在类体中和类体外定义成员函数的区别

在类体中定义的成员函数会自动成为内联函数,在类体完定义的不会。当然,在类体内部定义的函数也可以加 inline 关键字,但这是多余的,因为类体内部定义的函数默认就是内联函数。

内联函数一般不是我们所期望的,它会将函数调用处用函数体替代,所以建议在类体内部对成员函数作声明,而在类体外部进行定义,这是一种良好的编程习惯。

2.3 C++ 类成员的访问权限以及类的封装

C++ 通过 public、protected、private 三个关键字来控制成员变量和成员函数的访问权限,它们分别表示公有的、受保护的、私有的,被称为成员访问限定符所谓访问权限,就是你能不能使用该类中的成员。

在类的内部(定义类的代码内部),无论成员被声明为 public、protected、private,都是可以互相访问的,没有访问权限的限制。

在类的外部(定义类的代码之外),只能通过对象访问成员,并且通过对象只能访问 public 属性的成员,不能访问 private、protected 属性的成员。

示例:

类的声明和成员函数的定义都是类定义的一部分,在实际开发中,通常将类的声明放在头文件中,而将成员函数的定义放在源文件中。

成员变量大都以 m_ 开头,这是约定成俗的写法,不是语法规定的内容。以 m_ 开头既可以一眼看出这是成员变量,又可以和成员函数中的形参名字区分开。

2.3.1 简单的谈类的封装

private 关键字的作用在于更好地隐藏类的内部实现,该向外暴露的接口(能通过对象访问的成员)都声明为 public,不希望外部知道、或者只在类内部使用的、或者对外部没有影响的成员,都建议声明为 private。

根据 C++ 软件设计规范,实际项目开发中的成员变量以及只在类内部使用的成员函数(只被成员函数调用的成员函数)都建议声明为 private,而只将允许通过对象调用的成员函数声明为 public。

另外还有一个关键字 protected,声明为 protected 的成员在类外也不能通过对象访问,但是在它的派生类内部可以访问。

有读者可能会提出疑问,将成员变量都声明为 private,如何给它们赋值呢,又如何读取它们的值呢?

我们可以额外添加两个 public 属性的成员函数,一个用来设置成员变量的值,一个用来修改成员变量的值。上面的代码中,setname()、setage()、setscore() 函数就用来设置成员变量的值;如果希望获取成员变量的值,可以再添加三个函数 getname()、getage()、getscore()。

给成员变量赋值的函数通常称为 set 函数,它们的名字通常以set开头,后跟成员变量的名字;读取成员变量的值的函数通常称为 get 函数,它们的名字通常以get开头,后跟成员变量的名字。

除了 set 函数和 get 函数,在创建对象时还可以调用构造函数来初始化各个成员变量,不过构造函数只能给成员变量赋值一次,以后再修改还得借助 set 函数。

这种将成员变量声明为 private、将部分成员函数声明为 public 的做法体现了类的封装性。所谓封装,是指尽量隐藏类的内部实现,只向用户提供有用的成员函数。

2.3.2 对 private 和 public 的更多说明

声明为 private 的成员和声明为 public 的成员次序任意,既可以先出现 private 部分,也可以先出现 public 部分。如果既不写 private 也不写 public,就默认为 private。

在一个类体中,private 和 public 可以分别出现很多次。每个部分的有效范围到出现另一个访问限定符或类体结束时为止。

2.4 C++ 构造函数

在 C++ 中,有一种特殊的成员函数,它的名字和类名相同,没有返回值,不需要用户显式调用(用户也不能调用),而是在创建对象时自动执行。这种特殊的成员函数就是构造函数(Constructor)

构造函数必须是 public 属性的,否则创建对象时无法调用。

构造函数没有返回值,因为没有变量来接收返回值,即使有也毫无用处,这意味着:

  • 不管是声明还是定义,函数名前面都不能出现返回值类型,即使是 void 也不允许;
  • 函数体中不能有 return 语句。

2.4.1 构造函数的重载

和普通成员一样,构造函数是允许重载的。一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪一个构造函数。
构造函数的调用时强制性的,一旦在类中定义了构造函数,那么创建对象时就一定要调用,不调用是错误的。如果有多个重载的构造函数,那么创建对象时提供的实参必须和其中的一个构造函数匹配;反过来说,创建对象时只有一个构造函数会被调用。
构造函数在实际开发中会大量使用,它往往用来做一些初始化工作,例如对成员变量赋值、预先打开文件等。

2.4.2 默认构造函数

如果用户自己没有定义构造函数,那么编译器会自动生成一个默认的构造函数,只是这个默认的构造函数的函数体是空的,也没有形参,也不执行任何操作。

一个类必须有构造函数,要么用户自己定义,要么编译器自动生成。一旦自己定义了构造函数,不管有几个,也不管形参如何,编译器都不再自动生成。

实际上编译器只有在必要的时候才会生成默认构造函数,而且它的函数体一般不为空。默认构造函数的目的是帮助编译器做初始化工作,而不是帮助程序员。这是 C++ 的内部实现机制,这里不再深究。

最后需要注意的一点是,调用没有参数的构造函数也可以省略括号。 例如:Student stu() 或 Student stu,它们都会调用构造函数 Student()。

2.5 C++ 构造函数初始化列表

构造函数的一项重要功能是对成员变量进行初始化,为了达到这个目的,可以在构造函数的函数体中对成员变量一一赋值,还可以采用初始化列表

1. class Student{
2. 	...
3. 		public:
4.  		Student(char *name, int age, float score);
5. };
6.  
7. // 采用初始化列表
8. Student :: Student(char *name, int age, float score) : m_name(name), m_age(age), m_score(score){
9. 		// TODO:
10. }
11.  
12. int main(){
13. 		Student stu("小明", 15, 92.5f);
14. 		...
15. 		return 0;
16. }

使用构造函数初始化列表并没有效率上的优势,仅仅是书写方便,尤其是成员变量较多时,这种写法非常简单明了。

初始化列表可以用于全部成员变量,也可以只用于部分成员变量。

注意,成员变量的初始化顺序与初始化列表中列出的变量顺序无关,它只与成员变量在类中声明的顺序有关。 请看代码:

1. class Demo{
2. private:
3. 		int m_a;
4. 		int m_b;
5. public:
6. 		Demo(int b);
7. };
8.  
9. Demo :: Demo(int b) : m_b(b), m_a(m_b) { }
10. 
11. int main(){
12. 		Demo obj(100);
13. 		return 0;
14. } 

在初始化列表中,我们将 m_b 放在了 m_a 的前面,看起来是先给 m_b 赋值,再给 m_a 赋值,其实不然!成员变量的赋值顺序由它们在类中的声明顺序决定,在 Demo 类中,我们先声明的 m_a,再声明的 m_b,所以构造函数和下面的代码等价:

1. Demo :: Demo(int b) : m_b(b), m_a(m_b){
2. 		m_a = m_b;
3. 		m_b	= m_a;
4. }

给 m_a 赋值时,m_b 还未被初始化,它的值是不确定的,所以输出的 m_a 的值是一个奇怪的数字;给 m_a 赋值完成后才给 m_b 赋值,此时 m_b 的值才是 100。

obj 在栈上分配内存,成员变量的初始值是不确定的。

2.5.1 初始化 const 成员变量

构造函数初始化列表还有一个很重要的作用,那就是初始化 const 成员变量。初始化 const 成员变量的唯一方法就是使用初始化列表。 例如(变长数组):

1. class VLA{
2. private:
3. 		const int m_len;
4. 		int *m_arr;
5. public:
6. 		VLA(int len);
7. };
8.  
9. // 必须使用初始化列表来初始化 m_len
10. VLA :: VLA(int len) : m_len(len){ 
11. 		m_arr = new int[len];
12. }

2.6 析构函数

创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等,这个函数就是析构函数。

析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显示调用(也没法显示调用),而是在销毁对象时自动执行。 构造函数的名字和类名相同,而析构函数的名字是在类名前加一个 ~ 符号。

注意:析构函数没有参数,不能被重载,因此一个类只能有一个析构函数。如果用户没有定义,编译器会自动生成一个默认的析构函数。

函数名是标识符的一种,原则上标识符的命名中不允许出现 ~ 符号,在析构函数的名字中出现的~可以认为是一种特殊情况,目的是为了和构造函数的名字加以对比和区分。

C++ 中的 new 和 delete 分别用来分配和释放内存,它们与 C 语言中 malloc()、free() 最大的一个不同之处在于:用 new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数。构造函数和析构函数对于类来说是不可或缺的,所以在 C++ 中鼓励使用 new 和 delete。

2.6.1 析构函数的执行时机

析构函数在对象被销毁时调用,而对象的销毁时机与它所在的内存区域有关。

在所有函数之外创建的对象是全局对象,它和全局变量类似,位于内存分区中的全局数据区,程序在结束执行时会调用这些对象的析构函数。

在函数内部创建的对象是局部对象,它和局部变量类似,位于栈区,函数执行结束时会调用这些对象的析构函数。

new 创建的对象位于堆区,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。

2.7 C++ this指针

this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。

所谓当前对象,是指正在使用的对象。例如对于 stu.show(); ,stu 就是当前对象,this 就指向 stu。

示例:略

this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。

注意:this 是一个指针,要用 -> 来访问成员变量或成员函数。

this 虽然用在类的内部,但是只有在对象被创建以后才会给 this 赋值。

几点注意:

  • this 是 const 指针,它的值是不能被修改的,一切企图修改该指针的操作,如赋值、递增、递减等都是不允许的。
  • this 只能在成员函数内部使用,用在其他地方没有意义,也是非法的。
  • 只有当对象被创建后 this 指针才有意义,因此不能在 static 成员函数中使用。

2.7.1 this 到底是什么

this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。

this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。

成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息,所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。这个额外的参数,实际上就是 this,它是成员函数和成员变量关联的桥梁。

2.8 C++ 静态成员变量

对象的内存中包含了成员变量,不同的对象占用不同的内存,这使得不同对象的成员变量相互独立,它们的值不受其他对象的影响。

可是有时候我们希望在多个对象之间共享数据,对象 a 改变了某份数据后对象 b 可以检测到。共享数据的典型使用场景是计数,以前面的 Student 类为例,如果我们想知道班级中共有多少名学生,就可以设置一份共享的变量,每次创建对象时让该变量加 1。

在 C++ 中,我们可以使用静态成员变量来实现多个对象共享数据的目标。静态成员变量是一种特殊的成员变量,它被关键字 static 修饰,,例如:

1. ...
2. static int m_total;// 静态成员变量
3. ...

static 成员变量属于类。不属于某个具体的对象,即使创建多个对象,也只为 m_total 分配一份内存,所有的对象使用的都是这份内存中的数据。当某个对象修改了 m_total,也会影响到其他对象。

static 成员变量必须在类声明的外部初始化,具体形式为:

type class :: name = value;

type 是变量的类型,class 是类名,name 是变量名,value 是初始值。

静态成员变量在初始化时不能再加 static,但必须要有数据类型。被 private、protected、public 修饰的静态成员变量都可以用这种方式初始化。

注意:static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在(类外)初始化是分配。反过来说,没有在类外初始化的 static 成员变量不能使用。

static 成员变量既可以通过对象来访问,也可以通过类来访问。

注意:static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。 具体来说,static 成员变量和普通的 static 变量类似,都在内存分区中的全局数据区分配内存。

几点说明:

  • 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。
  • static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。
  • 静态成员变量必须初始化,而且只能在类体外进行。初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化为 0。全局数据区的变量都有默认的初始值 0,而动态数据区(堆区、栈区)变量的默认值是不确定的,一般认为是垃圾值。
  • 静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。

2.9 C++ 静态成员函数

在类中, static 除了可以声明静态成员变量,还可以声明静态成员函数。普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员

编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。

普通成员变量占用对象的内存,而静态成员函数没有 this 指针,不知道指向哪个对象,无法访问对象的成员变量,也就是说静态成员函数不能访问普通成员变量,只能访问静态成员变量。

普通成员函数必须通过对象才能调用,而静态成员函数没有 this 指针,无法在函数体内部访问某个对象,所以不能调用普通成员函数,只能调用静态成员函数。

静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。

示例:略

在 C++ 中,静态成员函数的主要目的是访问静态成员。和静态成员变量类似,静态成员函数在声明时要加 static,在定义时不能加 static。静态成员函数可以通过类来调用(一般都是这样做),也可以通过对象来调用。

2.10 C++ const 成员变量和成员函数

在类中,如果你不希望某些数据被修改,可以使用 const 关键字加以限定。const 可以用来修饰成员变量和成员函数。

2.10.1 const 成员变量

const 成员变量的用法和普通 const 变量的用法类似,只需要在声明时加上 const 关键字。初始化 const 成员变量只有一种方法,就是通过构造函数的初始化列表。

2.10.2 const 成员函数

const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值,这种措施主要是为了保护数据而设置的。const 成员函数也称为常成员函数

我们通常将 get 函数设置为常成员函数。读取成员变量的函数名字通常以 get 开头,后跟成员变量的名字,所以通常将它们称为 get 函数。

常成员函数需要在声明和定义的时候在函数头部的结尾加上 const 关键字,例如:

需要强调的是,必须在成员函数的声明和定义处同时加上 const 关键字。 char *getName() const 和 char *getName() 是两个不同的函数原型,如果只在一个地方加 const 会导致声明和定义处的函数原型冲突。

最后再来区分一下 const 的位置:

  • 函数开头的 const 用来修饰函数的返回值,表示返回值是 const 类型,也就是不能被修改。
  • 函数头部的结尾加上 const 表示常成员函数,这种函数只能读取成员变量的值,而不能修改成员变量的值。

2.11 C++ const 对象

在 C++ 中,const 也可以用来修饰对象,称为常对象。一旦将对象定义为常对象后,就只能调用类的 const 成员(包括 cosnt 成员变量和 const 成员函数)了。

定义常对象的语法和定义常量的语法类似:

const class object(params);
class const object(params);

当然也可以定义 const 指针:

const class *p = new class(params);
class const *p = new class(params);

class 为类名,object 为对象名,params 为实参列表,p 为指针名。两种方式定义出来的对象都是常对象。

一旦将对象定义为常对象之后,不管哪种形式,该对象就只能访问被 const 修饰的成员了(包括 const 成员变量和 const 成员函数),因为非 const 成员可能会修改对象的数据(编译器也会这样假设),C++ 禁止这样做。

2.12 C++ 友元函数和友元类(C++ friend 关键字)

在 C++ 中,一个类可以有 public、protected、private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问类的 private 成员。但是还有一种例外情况----友元(friend)。借助友元,可以使得其他类中的成员以及全局范围内的函数访问当前类的 private 成员。

friend 的意思是朋友,或者说是好友,与好友的关系显然要比一般人亲密一些。我们会对好朋友敞开心扉,倾诉自己的秘密,而对一般人会谨言慎行,潜意识里就自我保护。在 C++ 中,这种友好关系可以用 friend 关键字指明,中文多译为“友元”,借助友元可以访问与其有好友关系的类中的私有成员。

2.12.1 友元函数

在当前类外定义的、不属于当前类的函数也可以在类中声明,但要在前面加 friend 关键字,这样就构成了友元函数。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。

友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的。

1)将非成员函数声明为友元函数。示例:

1. class Student{
2. ...
3. public:
4. 		friend void show(Student *pstu);// 声明为友元函数
5. };
6.  
7. void show(Student *pstu) {...}
8. int main() {...}

以上 show() 是一个全局范围内的非成员函数,它不属于任何类。

注意:友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。

成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象成员;而 show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,想要明确这一点,就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。

2)将其他类的成员函数声明为友元函数
friend 函数不仅可以是全局函数(非成员函数),还可以是另外一个类的成员函数。示例:

略略略略略略略略略

2.13 C++ class 和 struct 的区别

C++ 中保留了C语言的关键字,并且加以扩充。在C语言中,struct 只能包含成员变量,不能包含成员函数。而在 C++ 中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。

C++ 中的 struct 和 class 基本都是通用的,唯有几个细节不同:

  • 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
  • class 继承默认是 private 继承,而 struct 继承默认是 public 继承。
  • class 可以使用模板,而 struct 不能。

在编写 C++ 代码时,强烈建议使用 class 来定义类,而使用 struct 来定义结构体,这样做语义更加明确。

2.14 C++ string 详解(C++ 字符串)

C++ 大大增强了对字符串的支持,除了可以使用C风格的字符串,还可以使用内置的 string 类。string 类处理起字符串来会方便很多,完全可以代替C语言中的字符数组或字符串指针。

string 是 C++ 中常用的一个类,它非常重要。

使用 string 类需要包含头文件,下面的例子介绍了几种定义 string 变量(对象)的方法:

1. #include <string>
2. #include <iostream>
3. using namspace std;
4.  
5. int mai(){
6. 		string s1;
7. 		string s2 = "c plus plus";
8. 		string s3 = s2;
9. 		string s4(5, 's');
10.		return 0; 
11. }

变量 s1 只是定义但没有初始化,编译器会将默认值赋给 s1,默认值是"",也即空字符串。

变量 s2 在定义的同时被初始化为"c plus plus"。与C风格的字符串不同,string 的结尾没有结束标志’\0’。

变量 s3 在定义的时候直接用 s2 进行初始化,因此 s3 的内容也是"c plus plus"。

变量 s4 被初始化为由 5 个’s’字符组成的字符串,也就是"sssss"。

与C风格的字符串不同,当我们需要知道字符串长度时,可以调用 string 类提供的 length() 函数。

2.14.1 转换为C风格的字符串

虽然 C++ 提供了 string 类来替代C语言中的字符串,但是在实际编程中,有时候必须要使用C风格的字符串(例如打开文件时的路径),为此,string 类为我们提供了一个转换函数 c_str(),该函数能够将 string 字符串转换为C风格的字符串,并返回该字符串的 const 指针(const char*)。请看下面的代码:

1. string path = "D:\\demo.txt";
2. FILE *fp = fopen(path.c_str(), "rt");

为了使用C语言中的 fopen() 函数打开文件,必须将 string 字符串转换为C风格字符串。

2.14.2 string 字符串的输入输出

string 类重载了输入输出运算符,可以像对待普通变量那样对待 string 变量,也就是用 >> 进行输入,用 << 进行输出。示例:

虽然我们输入了两个由空格隔开的网址,但是只输出了一个,这是因为输入运算符>>默认会忽略空格,遇到空格就认为输入结束,所以最后输入的http://vip.biancheng.net没有被存储到变量 s。

2.14.3 字符串的拼接

有了 string 类,我们可以使用 + 或 += 运算符来直接拼接字符串,非常方便,再也不需要使用C语言中的 strcat()、strcpy()、malloc() 等函数来拼接字符串了,再也不用担心空间不够会溢出了。

用+来拼接字符串时,运算符的两边可以都是 string 字符串,也可以是一个 string 字符串和一个C风格的字符串,还可以是一个 string 字符串和一个字符数组,或者是一个 string 字符串和一个单独的字符。示例:

2.14.4 string 字符串的增删改查

参考链接:http://c.biancheng.net/cplus/class/

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值