乐闻世界logo
搜索文章和话题
C++入门笔记(一)

C++入门笔记(一)

爱糖果的Kitty的头像
爱糖果的Kitty

2023年12月05日 09:51· 阅读 419

一、C++语言与思路介绍

1、封装

(1)封装意味着把对象的属性和方法结合成一个独立的系统单位,并尽可能隐藏对象的内部细节。 (2)封装是面向对象思想描述的基础,从此程序员面对的就不百是许多盒杂的函数和过程实现,而是少数具有行为能力的个体实例。

2、抽象

(1)抽象的过程是对具体问题进行概括的过程,是对一类公共问题进行统一描述的过程。为了使某些必要的信息得以顺利的交流,设计者必须制定一个抽象,就如同一个协议,一个得到所有参与活动的有效个体支持的协议。

3、继承

(1)子类对象拥有与其基类相同的全部属性和方法,称为继承。

4、多态

(1)多态是指在基类中定义的属性和行为被子类继承后,可以具有不同的数据类型或者表现行为等特性。 (2)加入动物是一个基类,它的子类有老虎、兔子、狗、小马⋯ (3)那么作为动物有一个行为定义为Movel(),那云这些子类继承下来后会根据自己的特性采取不同个性的Move0方式。

二、分析

1、程序分析

(1)"Hello world"程序展示了C++对象第一次使用。对象在哪里?就是这个"cout"。

(2)cout 是一个输出流对象,它是"console out(控制台输出)"的缩写。是属于basic ostream 类的对象。ostream 类在iostream头文件中定义。注意······

(3)什么是输出流?其实也就是一个概念,在C++中引入了很多类似的概念:例如数据可以从键盘流入程序,又可以从程序流向屏幕、打印机等~

(4)using namespace std; 这条指定带给我们一个全新的概念:命名空间。就是C++标准库所使用的所有标识符(即类函数、对象等的名称)都是在同一个特殊的名字空间(std)中来定义的。如果我们没有使用这条指令,我们将需要使用std::cout 这样的语法来调用输出流对象。

2、流操作符

不知道大家有没有注意到 "<<"这个符号很眼熟?这个操作符不是C语言里边位运算的左移操作符吗?难道C++里边改规则了?事实上不是这样的,它只不过体现了C++的另一个特点:支持重载。重载事实上就是允许我们按照不同的方式使用同一个操作符。我们后边会详细讲解。

3、C++版本分析

(1)流对象cin的类型是istream,它知道如何从用户终端读取数据。 (2)cin >i; cin 输入操作符又称为提取操作符,它一次从输入流对象cin 提取一个整数。当用户进行键盘输入时,对应的字符拚输入到操作系统的键盘缓冲区中。这样,当用户点击键盘上的"Enter"键时,操作系统把键盘缓冲区的内容传输到cin流的内部缓冲区,">>"操作符道后从这个缓冲区提取需要的信息。

(3)我们还发觉,如果用户不进行键盘的输入,程序拚会阳塞。这是阻塞性 (blocking)IO的例子。 (4)">>"最初定义为右移操作符,它在C++ 中进行了重载,当它按照这里所示方式使用时,它就用于从输入流对象提取信息。另外,这个操作符对所有内建的数据类型都进行了重载,所以它可以从输入流对象提取出int,float, double 型数据,也可以提取字符串等数据。

【重载:就是赋予一个旧事物新的意义】

(5)在while( cin >> i)中,表达式cin >> i返回输入流对象本身,也就是cin。但是,如果到达了文件尾或者提取操作符遇到一个非法值,这个返回值将是false。我们小举一个列子:如果我们试图把一个浮点数读取到一个int 型变量中,当提取操作符遇到小数点时,它将使输入流对象置于一个错误的状态中,导致 cin 的值为false。

(6)注意,在while( cin >> i)中,当用户在键盘上点击"enter"键的时候,在这一句并不会结束。另外,观察两个程序,我们发现:C和C++事实上在声明变量的事实上有点不同,C++允许我们在程序的任意位置声明变量。这大大的提高了大型C++程序的可读性,因为这样子我们就可以在实际需要使用变量的时候才来声明他们。

4、cin、cout对象的多个方法

(1)前边我们讲过,cin 是istream类的对象,那么cin 同样具有一些方法,这些方法可以增强对于输入操作的控制,下边我们将通过简单的列子介绍cin 的一些方法!

  • 例一:cin.ignore() & cin.getline()
c
#include <iostream> using namespace std; int main () { char buf[20]; cin.ignore(7); cin.getline(buf, 10); cout << buf << endl; return 0; }

cin.ignore(7)是忽略前7个字符字符;

cin.getline(buf, 10)是接着获取10字符存到字符串buf中。

  • 例二:cin.get() & cin.peek()
c
#include <iostream> using namespace std; int main () { char p; cout << "请输入一段文本:\n"; while () { p = cin.get(); cout << p; } cout << endl; return 0; }

cin.peek()是在字符串中挑出一个字符,判断是否是‘回车’

cin.get()是将挑选出的字符放回去

  • 例三:cin.gcount() & cin.read()
c
#include <iostream> using namespace std; int main() { const int SIZE = 50; char buf[SIZE]; cout << "请输入一段文本:" cin.read(buf, 20); cout << "字符串收集到的字符数为:" << cin.gcount() << endl; cout << "输入的文本信息是:"; cout.write(buf, 20); cout << endl; return 0; }

5、C++的文件操作

(1)由于我们这个C++ 的版本相对有点不同,对类和对象的应用比较多和烦,但是由于我们现在还没开始讲解类和对象,所以大家现在只管“先用”,暂时可以不求甚解!等到实际学了类和对象,大家回过头来看,就明白了一切!有些朋友可能会问:为什么不先把类和对象讲清楚呢?我们依旧老规矩,以实列为主,以事实说话:

c
#include <fstream> #include <iostream> using namespace std; int main() { ifstream in; in.open( "test.txt" ); if( !in ) { cerr << "打开文件失败"<endl; return 0; } char x; while( in >> X ) { cout << x; } cout << end1; in.close(); return 0; }

从刚刚的例题我们得到的信息是C++由于有类的封装,很多东西都变得更加“仔细”了!上边的例题我们用到的是文件的读取类ifstream.。接着我们结合列题来说说文件的写入要用到的类 ofstream。

c
#include <fstream> #include <iostream> using namespace std; int main() { ofstream out; out.open( "test.txt" ); if () { cerr<< "打开文件失败!" <<endl; return 0; } for( int i=0; i < 10; i++ ) { out << i; } out << endl; out.close(); return 0; }

在前边两个例子中我们出现:

c
ifstream in; in.open( "test.txt" );

c
ofstream out; out .open( "test.txt" );

它们都是用一个open 函数来完成打开文件的功能。当然,这不是唯一的方法,我们还可以文样实现。

c
ifstream in( "test.txt" );

c
ofstream out( "test.txt" );

请大家自行体验。 以上代码在创建一个ifstream和ofstream类的对象时,将文件的名字传递给它们的构造函数。暂时我们可以这么理解构造函数:就是对象默认使用的函数(方法)。

那么这两种方法有什么区別吗?结论是没有区别! 事实上它还可以接受不止一个参数!下边我们给出一个接受两个参数的实列:

c
ifstream in( char* filename, int open_mode)

其中,filename 表示文件的名称,它是一个字符串;open_mode 表示打开模式,其值用来定义以怎样的方式打开文件(跟open的参数一样哈)。

下面给出几种常见的打开模式:

shell
ios::in —— 打开一个可读取文件 ios::out —— 打开一个司写入文件 ios::binary —— 以二进制的开式打开一个文件 ios::app —— 写入的所有数据将被追加到文件的末尾 ios::trunk —— 删除文件原来已存在的内容 ios::nocreate —— 如果要打开的文件并不存在,那么以此参数调用open 函数将无法进行 ios:noreplece —— 如果要打开的文件已存在,试图用open 函数打开时将返回一个错误

三、函数的重载

C++里的函数重载(overloading),机制比我们此前见到的东西都高深,这种语言的灵活性和强大功能在它身上体现得淋漓尽致。所谓函数重载的实质就是用同样的名字再定义一个有着不同参数但有着同样用途的函数。(人格分裂、多重身份······) 注意:可以是参数个数上的不同,边可以是参数数据类型上的不同!

有以下几点需要大家注意的: (1)对函数(方法)进行重载一定要谨慎,不要“无的放矢”或“乱点鸳鸯”; (2)要知道重载函数越多,该程序就越不容易看懂; (3)注意区分重载和覆盖(覆盖后边我们会讲到); (4)我们只能通过不同参数进行重载,但不能通过不同的返回值(尽管后者也是一种区别); (5)最后,对函数进行重载的目的是为了方便对不同数据类型进行同样的处理。

四、复杂的数据类型

本讲我们将讨论三种数据类型:数组、指针(一种更加重要和抽象的数据类型)、结构(它的用法和用途可以让大家对西向对象的编程技术有基本的印象〕

1、数组

(1)数组是许多程序设计语言的重要组成部分,龙其在C程序中经常会碰到它们。 (2)数组的优点在于,一个数组可以把许多个同类型的值存储在同一个变量名下。 (3)回顾一下,数组仍需要被声明为某一种特定的类型:float,char,int 。 (4)type name[x]; 【注意:我们不会把不同数据类型的数据混杂保存在同一个数组中,就像猫和狗搞在一起会出事一样的道理。】

【例题实践】

定义一个数组容纳10个整数,这些整数来自用户输入。我们将计算这些值的累加和、平均值并输出。

c
//复杂的数据类型——数组 #include <iostream> #define ITEM 10 //宏定义 int main() { int num[ITEM]; std::cout << "请输入" << ITEM << "个整型数据!\n\n"; for( int i=0; i < ITEM; i++) { std::cout << "请输入第" << i+1 << "个数据:"; while ( !(std::cin >> num[i]) ){ std::cin.clear; std::cin.ignore(100, '\n'); std::cout << "请输入一个合法的值!"; } } int total = 0; for( int j=0; j < ITEM; j++) { total += num[j]; } std::cout << "总和是:" <<total; std::cout << "\n平均值是:" << total / ITEM; return 0; }

在C 语言里,字符串被实际存储在一个字符数组中。我们在C++中我们也可以用同样的方法实现,但C++提供了更好的std:string 类型,所以我们不必再使用老式的C方法咯。

【例题实践】

请分别尝试用C 和C++ 实现将用户输入的字符串打印出来~

C
//复杂的数据类型——数组 //打印用户输入的字符串 #include <iostream> #include <string> int main() { std::string str; std::cout << "请随便输入一个字符串:"; //getline 就是获取一行数据 std::getline(std::cin, str); std::cout << str << "\n"; return 0; }

2、指针

我们的下一个〝复杂的"数据类型是C 语言与 C++里最重要的数据类型之一:指针。 其实在绝大数的高级语言中或多或少会使用指针,只是都被包装了起来。它和我们之前学过的其他变量都不一样,但确实可以帮助我们了解编程背后的很多不能说的秘密更重要的是指针可以让我们完成一些其他办法无法完成的任务。

我们首先要知道一件事情:程序在硬盘上以文件的形式存在,但它们的运行却是在计算机的内存里发生的! 以下为大家演示以下这些语句声明的变量在内存中的存放情况:

(1)对齐

问题:浮点型变量 C的值为什云是从内存地址8处开始存储,而不是从内存地址5处存储呢? 其实,在C++ 里,变量类型是根据它们的自然边界进行对齐的!不过这个我们只需知道即可,不需要太关心,因为编译器会自动帮我们处理这类问题。 另外,对齐问题会因为系统平台的不同而不同!

(2)寻址

在刚才,我们发现对于变量可以用两种办法来对他进行索 。 一种是通过变量名; 另一种是通过地址。 这里我们就要引入一个新的操作符,叫做“取址”操作符:"&",它的作用就是获得变量的地址。 我们习惯这么使用:

C
int var = 123; std::cout << "Address is : " << &var;
(3)使用指针

上边我们对地址这个概念是理解和使用指针的基础。地址是计算机内存中的某个位置,而指针是专门用来存放地址的特殊类型变量。 一般情况下我们用下边的形式来声明指针变量:type *pointerName; 例如:

C
int *p; int pp = 123; p = &pp;
【注意】

请看下边这条语句,思考其作用:int *p1, p2, p3; 注意,上边的形式声明的指针变量只有一个:p1,而p2和p3都是整型变量而己。 正确的做法:int *p1, *p2, *p3;

建议的做法:每行只声明一个指针变量,这样最不容易出错。不要害怕多个空格或者多几行编译出来的程序就变大了!

在创建指针时,空格放在哪里都是没关系的,下边的语句都是可以接受的:

C
int *p1; int * p1; int* p1;

指针变量前边的类型是用来说明指针指向的数椐的类型,请务必匹配来使用。另外允许void 类型的指针变量:void *p;

(4)“走后门” —— 利用指针改变值
  • 温故而知新: ——创建变量时,系统将分配一些内存块用来保存它们的值; ——每个内存块拥有一个独一无二的地址; ——变量的地址可以用 &variablename 语法来取得;(注:&我们称为“取地址〞操作符) ——可以把地址赌值给一种称为指针的特殊变量; ——指针的类型必须与由它保存其地址的变量的类型一致。
  • 接下来给大家介绍点真正的好东西:
    C
    int a = 456; char b = 'C'; int *aPointer = &a; char *Pointer = &b;
  • 这会让程序保留4个内存块,两个为变量保留,两个为指针保留。变量a和变量b里边存放的是变量的值;两个指针变量存放着指针的值,这些值是其他变量的地址。
  • 当我们知道了某个变量在内存中的地址(通过指针〕,就可以利用指针访问位于该地址的数据。
  • 这需要对指针进行“解引用(Deference)"处理:即在指针名的前面加上一个星号(*)。
  • 如:stdicout << *aPointer;
  • 这里我们来理解一下:把整数变量 a的地址存储在 aPointer 指针里之后,*aPointer 和变量a将代表同一个值。
  • 因此:*aPointer = 123;
【课后****思考】

1、一定要牢记的事实:指针所保存的是内存中的一个地址。它并不保存指向的数据的值本身。因此,务必确保指针对应一个已经存在的变量或者一块已经分配了的内存。

2、星号有两种用途,时常困惑了初学者:

——第一种是用于创建指针:[ex] int *myPointer = &myInt;

——第二种是对指针进行解引用:[ex]*myPointer = 3998;

  • C++允许指针群P,就是多个指针有同样的值:
    C
    int *p1 = &myInt; int *p2 = &myInt;
  • C++支持无类型 (void)指针,就是没有被声明为某种特定类型的指针,例如:void *vPointer;
  • 注意:对一个无类型指针进行解引用前,必须先把它转换为一种适当的数据类型。
(6)指针和数组
  • 在此之前关于地址和指针的例子中,我们使用的是标量类型:整数、实数和字符。

  • 当我们遇到一个标量类型的变量时,我们创建一个与其类型相同的指针来存放它的地址。可是,当我们遇到的是数组时,做法有所改变吗?

  • 我们知道,计算机把数组是以一组连续的内存块保存的,例如:int myArray[3] = {1,2,3};

  • 这就说明了数组拥有很多个地址,每个地址对应着一个元素。可能你会觉得要用指针指向一个数组,需要用到很多指针变量?

  • 其实在C/C+中,事实远没有想象那么困难。数组的名字其实也是一个指针(指向数组的基地址,就是第一个元素的地址)。

  • 就刚才的列子,以下两句做同样的事情:

    C
    int *ptri = &myArray[0]: int *ptr2 = myArray;
【例子】
C
//复杂的数据类型——指针和数组 #include <iostream> int main() { const unsigned short ITEMS = 5; int intArray[ITEMS] = {1, 2, 3, 4, 5}; char charArray[ITEMS] = {'F', 'i', 's', 'h', 'C'}; int *intPtr; char *charPtr; std::cout << "整型数组输出:" << '\n'; for( int i=0; i < ITEMS; i++ ){ std::cout << *intPtr << " at " << reinterpret_cast<unsigned long>(intPtr) << '\n'; intPtr++; } std::cout << "字符型数组输出:" << '\n'; for( int i=0; i < ITEMS; i++ ){ std::cout << *charPtr << " at " << reinterpret_cast<unsigned long>(intPtr) << '\n'; charPtr++; } return 0; }
【使用重载】
C
//数组和指针的应用——重载 #include <iostream> void print( int *pBegin, int *pEnd ) { while( pBegin != pEnd ) { std::cout << *pBegin; ++pBegin; } } void print( char *pBegin, char *pEnd ) { while( pBegin != pEnd ) { std::cout << *pBegin; ++pBegin; } } int main() { int num[5] = { 0, 1, 2, 3, 4 }; char name[5] = { 'F', 'i', 's', 'h', 'C' }; print(num, num+5); std::cout << '\n'; print( name, name+5 ); std::cout << '\n'; return 0; }
【对上面进行修改:泛型程序设计】
C
//数组和指针的应用——泛型程序设计 #include <iostream> template <typename elemType> //无需再考虑数据类型的不同 void print( elemType *pBegin, elemType *pEnd ) { while( pBegin != pEnd ) { std::cout << *pBegin; ++pBegin; } } int main() { int num[5] = { 0, 1, 2, 3, 4 }; char name[5] = { 'F', 'i', 's', 'h', 'C' }; print(num, num+5); std::cout << '\n'; print( name, name+5 ); std::cout << '\n'; return 0; }
总结
  • 指针运算的重要性在高级和抽象的程序设计工作中体现得更加明显(如刚才的泛型设计)。
  • 就目前而言,大家只需要记住数组的名字同时也是一个指向其第一个元素(基地址)的指针。
  • 数组可以是任何一种数据类型,这意味着我们完全可以创建一个以指针为元素的数组。

3、结构

  • C语言和C++有许多共同的优美之处。其中之一便是程序员不必受限于这两种语言自带的数据类型的束缚。
  • C和C++的程序员完全可以棍据具体情況定义一些新的数据类型并创建新类型的变量。
  • 事实上,这个概念一直贯穿于C+的核心:对象。
  • 但首先,我们讲一个比较简单的例子:结构。
  • 结构(Structure)是一种由程序员定义的、由其他变量类型组合而成的数据类型。
  • 定义一个结构的基本语法是:
    C
    struct name { type varNamel; type varName2; ..... };

(1)结构的应用

  • 当需要处理一些貝有多种属性的数据时,结构往往是很好的选择。
  • 例如当我们在编写一个档案管理程序时,涉及到的基本特征有:姓名、身份证、性別......
C
struct Manage { std::string name; std::string uid; char sex; // F==Female, M==Male }

注意:C++对于一个结构所能包含的变量的个数是没有限制的,那些变量通常我们成为该结构的成员,他们可以是任意一种合法的数据类型。回到刚才的列题,在定义了一个结构之后,就可以使用如下所示的语法来创建该类型的变量了:

C
Manage m; // 创建一个Manage结构类型m —— m.name = "管理" —— m.uid = "111"; —— m.sex = 'M';
  • 回顾一下刚才的做法: ——定义结构 ——用“.”对结构成员进行赋值
  • 如果我们在创建一个结构类型变量的时候就已经知道它各个成员相关的值,我们可以在声明新变量的同时进行赋值。
C
Manage m ={“管理”, “111”, 'M'}
  • 创建一个指向该结构的指针:
C
Manage *p = &m;
  • 注意:因为指针的类型必须与指向的地址的变量的类型一致,所上p指针的类型也是Manage
  • 方法一:我们可以通过对指针进行解引用来访问相应的变量值
C
(*p).name= 〝创建" (*p).id = "222";
  • 如果你觉得刚刚的方法不够地道不够味儿,可以换用第二种方法:
C
p->name=〝创建" p->id = "222"; p->sex = F; std::cout << p->name; std::cout << p->id; std::cout << p->sex;

五、传值、传址和传引用

  • 这一节我们对函数进行进一步的剖析讲解,希望给大家带来一些新鲜的感觉。
  • 在编写个人函数的时候,你将受到C++中一条基本原则的限制:在默认的情况下,参数只能以值传递的方式给函数。
  • 这句话的理解是:被传递到函数的只是变量的值,永远不会是变量本身。
  • 绕开 〝值传递〞问题的第一种方法是向函数传递变量的地址取代它的值。我们说C语言强大,有很大一部分就是在于他的灵活,他的灵活,有大一部分就是可以利用指针进行委婉地乱改。。。
  • 正如我们所理解的,想要获取某个变量的地址只需要在它前边加上一个“取地址”操作符(&),就行了。
  • 那我们可以试着这样:changeAge( &age, age+1)
C
#include <iostream> void changeAge( int age, int newAge ); //如果想要实现计划功能,需要怎么改? int main() { int age = 24; std::cout << "My age is " << age << " 岁" << '\n'; changeAge( age, age+1 ); std::cout << "Now my age is " << age << " 岁" << '\n'; } void changeAge( int age, int newAge ) { age = newAge; std::cout << "In this, my age is " << age << " 岁" << '\n'; }

注意:如果传过去的是地址,在函数中必须要通过 “ * ” 对指针进行解引用,除非你有其他用途。

C
#include <iostream> void swap( int *x, int *y ) { int temp; temp = *x; *x = *y; *y = temp; } int main() { int x; int y; std::cout << "请输入两个不同的值:"; std::cin >> x >> y; swap( &x, &y ); std::cout << "调整后输出:" << x << ' ' << y << ' ' << '\n'; return 0; }
  • 传址在我们看来已经是很不错,不过C++的大神们在完善的过程中完善了地址这个概念。
  • 设想:如果事先就知道某个函数的参数只能接受一个地址,能不能使用某种约定使得在调用该函数时不需要使用指针的语法呢?
  • 于是乎,以引用传递方式传递输入方式的概念因此而产生了。
  • 其实他跟我们这个传址的目的是一样的,都是把地址传递给函数,回语法不同更加容易使用了。

六、联合、枚举和类型别名

  • 在 C++ 里还有许多其他类型使我们暂时还没有提到的。到目前为止,我们已经见过了整数、实数、字符、字符串、数组、指针和结构。今后我们还会讨论一种优于数组的向量(vector)类型。
  • 这一节,我们介绍几个非主流的类型,第一全是:联合 (union)。

(1)联合

  • 联合与结构有很多相似之处,联含边可以容纳多种不同类型的值,但是它每次只能存储这些值中的某一个。
  • 例如:我们要定义一个变量来存放某种密码,我们可以选择是你女朋友的生日、身份证最后4位数字或者你养的宠物的名字等等。
  • 联合类型将是一个不错的选择 ——>
C
union mima { unsigned long birthday; unsigned short ssn; char* pet; };
  • 定义了这个联合类型之后,就可以像下面这样创建一个该类型的变量了:
C
mima mima_1;
  • 再接下来,我们可以像对结构成员进行赋值那样对联合里的成员进行赋值,使用同样的语法:
C
mima_1.birthday = 19881301;
  • 上边这条语句是将值19881301存入mima 1联合的birthday 里边。如果我们再执行下边语句:
C
mima_1.pet = "Chaozai";
  • 这个联合将把 "Chaozai" 存入mima_1联合的pet 成员,并丟弃binthday成员里的值。
C
//联合 #include <iostream> union mima { unsigned long birthday; unsigned short ssn; char* pet; //char*表示字符指针类型 }; int main() { mima mima_1; mima_1.birthday = 19980101; std::cout << mima_1.birthday << '\n'; mima_1.pet = "Chaozai"; //错误写法,字母不能用单引号。 //使用单引号后,表示一个整数 //mima_1.pet = 'Chaozai'; std::cout << mima_1.pet << '\n'; std::cout << mima_1.birthday << '\n'; return 0; }

(2)枚举

  • 枚举 (enum)类型用来创建一个可取值列表:
C
enum weekdays { Monday, Tuesday,Wednesday, Thursday, Friday };
  • 定义一个枚举类型之后,我们就可以像下面这样创建该类型的变量:
C
weekdays today;
  • 然后我们像下边的方式对他进行赋值:
C
today Thursday;
C
//枚举 #include <iostream> int main() { enum weekdays{ Monday, Tuesday, Wednesday, Tursday, Friday };; weekdays today; today = Monday; std::cout << today << '\n'; today = Tuesday; std::cout << today << '\n'; }
  • 注意,我们这里不需要使用引号,因为枚举值不是字符串。。。
  • 编译器会按照各个枚举值在定义时出现的先后顺序把它们与0~n-1的整数(n是枚举值的总个数)分别关联起来。
  • 使用枚举类型好处有两个: ——它们对变量的可取值加以限制; ——它们可以用做switch条件语句的case 标号。(因为字符串是不能作为标号用的!小技巧哦~)

(3)类型别名

最后我们来介绍 Typedef 保留字,使用它可以为一个类型定义创建一个别名。 例如,我们不喜欢更用int* 来创建指针,可以像下边这样定义一个类型别名:

C
typedef int* intointer;

在此之后,我们就可以像下面这样来定义整型指针了:

C
intPointer myPointer:

七、对象

  • 我们从一个类开始,首先类是一个模型。当我们为这个类创建实列的时候,也就是对象本身。
  • 这跟我们之前讲解的定义和使用结构的过程很相似,但是这更有扩属性和前瞻性。
  • 对于初学者而言,我们先给大家一个区别:对象的内部可以有变量和函数,而结构通常只由各种变量构成。
  • 我们知道,要找对象,内心先要有对象的模型不然怎么知道你找到后是猪扒还是牛扒?!
创建简单的类
  • 因此,我们首先需要知道的一件事情是如何编写一个简单对象的模型 - 类
  • 类(Class)就像是一副蓝图,它决定一个对象将是什么样的(具备什么样的属性、功能)。
  • 所以OOP过程的第一步是创建一个类,而每个类跟变量一样都有一个名字,我们就从如何声明一个类说起:
C
class MyFirstClass {};
  • 就这样,我们创建了一个类!蛋然它什么都干不了,回它是一个起点,一个成功的开始。
  • 注意,类名的第一个字母采用大写是一种习惯的上的标准,回不是硬性规定。还有在类声明末尾必须有一个分号,这一点跟C++结构情况相同。
  • 类由变量和函数组成,对泉将页用那些变量来存储信息,调用那些函数来完成操作。所以人们常常会看到一些专门术语:类里边的变量成为属性,函数成为方法。注意,他们的本质没有改变。
让我们来造车
C
class Car { public: stdistring color; std::string engine; float gas_tank; unsigned int Wheel; } // 先动手,别等咯~
  • 我们需要在声明变量之前先写出单词"public:"稍后我们将对此做法的理由作出解释!
  • 刚刚我们声明了一辆车的简单属性,现在我们应该让他能跑起来吧?
  • 那么我们应该为类定义一些方法,其实也就是定义一些函数黑了。创建个人函数也是两个步骤的过程:先创建函数的原型(吉明),再描述该函数本身实现过程。
  • 给类添加方法如出一辙: ——先在类的声明里创建一个方法的原型 ——稍后再实现这个方法
C
class Car { public: stdistring color; stdistring engine; float gas_tank; unsigned int Wheel; void fill tank( float liter ); //方法的声明:方法是”加油,会数是公升" }

现在我们的Car类有了一个名为fiTank的方法,它只有一个输入参数,不需要任何返回值。 但是我们只有他的原型(声明),想要使用它,我们还需要对这个函数进行正式的定义(即告诉系统如何去实现它)。方法的定义通常安排在类声明的后面:

C
void Car fillTank(float liter) { gas_tank += liter; }
  • 我们发觉作用城解析操作符(::),作用是告诉编译器这个方法存在于何处,或者说是属于哪一个类。
  • 其实我们对这个不应该感到陌生,从一开始就不提召using namespace std; 这样偷懒的做法,所以我们是std::cout。。。
  • 事实上std:cout所引用的是std里定义的cout,而std::string数据类型其实也是一个对象。我们一直在使用对象,只是自己还不知道罢了。
C
//创建一个类 #include <iostream> #include <windows.h> #define FULL_GAS 85 class Car { public: std::string color; std::string engine; unsigned int gas_tank; unsigned int wheel; //方法的声明:方法是“加油”,参数是“公升” void setColor( std::string col ); void setEngine( std::string eng ); void setWheel(unsigned int whe ); void fillTank( int liter ); int running( void ); void warning( void ); }; void Car::setColor( std::string col ) { color = col; } void Car::setEngine( std::string eng ) { engine = eng; } void Car::setWheel(unsigned int whe ) { wheel = whe; } void Car::fillTank( int liter ) { gas_tank += liter; } int Car::running( void ) { std::cout << "我正以120的时速往前移动\n"; gas_tank--; std::cout << "当前还剩" << 100*gas_tank/FULL_GAS << "%" << "油量!"; return gas_tank; } void Car::warning( void ) { std::cout << "WARNING!!" << "还剩" << 100*gas_tank/FULL_GAS << "%" << "油量!"; } int main() { char i; Car mycar, car1; mycar.setColor("WHILE"); mycar.setEngine("V8"); mycar.setWheel(4); mycar.gas_tank = FULL_GAS; while( mycar.running() ) { if( mycar.running() < 10 ) { mycar.warning(); std::cout << "请问是否需要加满油再行驶?(Y/N)\n"; std::cin >> i; if( 'Y' == i || 'y' == i ) { mycar.fillTank(FULL_GAS); } } } return 0; }

待续.....

标签: