本次学习涵盖了八个章节的知识点,认识了面向对象的基本操作,会做一些简单的小系统,比以往更广泛地了解了C++语言。首先是主要的知识点介绍,最后是我的一些学习感想。
关于知识结构,我想把类和对象的定义放在前面
对象:是应用领域中明确角色的实体,有状态、行为和标识。
类:为描述一组对象在结构和行为上的共性,可以创建抽象数据类型,对一组具有相同结构和行为的对象的抽象描述,大致分为属性、操作。
一、指针和引用
(一)指针的特点
指针持有一个对象的地址,称为指针“指向”该对象;通过指针可以间接操纵它指向的对象。
(二)定义指针变量的语法
类型 指针变量;
每个指针都有相关的类型,要在定义指针时指出,例如:int pi;
(三)取地址运算符“&”
指针存放指定类型对象的地址,要获取对象的地址,使用取地址运算符“&”。
int ival = 120;
int *pi = &ival; // pi存放int变量ival的地址
// 或者说pi指向ival
char ch = ‘a’, *pc = &ch;
// pc指向字符型变量ch
(四)const限定指针
指向const对象的指针(非const ):const type cp; 或者type const cp;
cp是指向常量的指针,它所指向的内存中的内容不可以改变,即cp的值不能改变。
指向非const对象的const指针:type const cp = initAddressValue;
cp是常量指针,初始化后值不能改变,指向固定的单元。
二、结构体和string
(一)结构体
结构体把一组来自不同类型的数据组合在一起构成复合类型,其中的每个数据都是结构体的成员。
结构体由关键字struct定义,语法形式:
struct 结构体类型名{
成员声明;
};
结构体的成员不能独立使用,必须由结构体类型的变量通过成员选择运算符“.”来选择,或者由结构体类型的指针通过“->”运算符选择。
结构体默认数据类型、成员函数为公有。
(二)标准库类型string
string 表示可变长度的字符序列
字符串是对象,使用string 类时要包含头文件
string 类的功能大致如下:
1、各种初始化方式
2、字符串之间的复制、比较、连接
3、查询字符串长度和判断字符串是否为空
4、访问字符串中的单个字符
String可以看作是字符串数组,可通过遍历让其输出全部字符串,如:
cout <<“Length of s1 is :” << s1.size() << endl;//逐个输出s1中的字符
for (size_t i = 0; i < s1.size(); ++i)
cout << s1[i] <<" “;
关于String的读写,这里主要介绍两种方法:while(cin)和getline()函数
1、//读取输入流中的单词,直到文件结束,即遇到空格返回循环
string word;
while(cin >> word)
cout << word << endl;
2、//每次读取一行文本,直到文件结束
string line;
while(getline(cin, line))
cout << line << endl;
获取string对象的长度:函数.size()或者.length()
判断stirng对象是否为空:stringObj.empty();
比较string对象:可以用关系运算符比较两个字符串对象
两个string相等意味着它们的长度相同,并且所包含的字符也完全相同,字符串的大小关系依照字典顺序定义且区分大小写字母。
string s1 = “hello”;
string s2 = “hello world”; // s2 > s1
string s3 = “Hello”; // s3 < s1, s3 < s2
(三)标准库类型vector
1、vector的特点:
表示对象的集合,其中所有对象的类型都相同,可以通过索引访问各个对象;vector是对象,其中容纳着其他对象,被称为容器。
2、vector的用法:
vector是长度可变的向量,可替代内置数组,要使用vector,必须包含头文件。
3、定义和初始化vector:
//列表初始化
vector svec1{“how”, “are”, “you”}; //3个元素
vector svec2(“how”, “are”, “you”); //错误:括号
//创建指定数量的元素
vector ivec(10, 1); //ivec包含10个int元素都初始化为1
vector svec(10, “hi”); //10个string对象"hi”
//值初始化
vector ivec(10); //ivec包含10个int元素都初始化为0
vector svec(10); //svec包含10个string对象,默认为空
4、在vector中添加和删除元素
vector的大小可以动态变化,并且可以在初始化时指定vector对象的大小和初始值;常见用法是创建一个空vector,根据需要动态添加元素。
增加:push_back()
将一个值添加到vector的末尾,并使vector的大小增加
删除:pop_back()
删除vector对象末尾的元素
(四)迭代器
1、定义:迭代器类似于指针类型,提供对对象的间接访问,在容器或string对象上使用,指向的对象是容器中的元素或string中的字符。
访问标准容器的元素的通用方法是使用迭代器;可以访问容器中的某个元素,也可以在容器上移动。
标准库容器迭代器的类型:iterator、const_iterator
2、begin()和end()
begin()返回指向第一个元素或字符的迭代器
end()则返回指示容器或string的最后一个元素的下一个位置的迭代器
begin()返回指向第一个元素或字符的迭代器
end()则返回指示容器或string的最后一个元素的下一个位置的迭代器
(五)文件流
具体步骤:
(1)新建一个文件流对象
读文件用ifstream,写文件用ofstream;
(2)把文件流对象和文件关联起来
打开文件,使用文件流的open函数;
可以指定打开文件的模式
(3)操作文件流
使用与终端I/O相同的操作读写文件
(4)关闭文件流
使用文件流的close函数
三、函数
(一)基本定义
函数是一个命名的代码块,通过调用函数可以执行相应的代码,用来实现各种算法,完成特定的任务。
包括库函数,程序员自定义的函数。
C++程序由函数构成,函数之间通过传递参数和返回值进行通信。
函数定义的语法形式为:
返回类型 函数名(参数列表) { 函数体 }
返回类型为void无需返回内容。
(二)参数传递
参数传递是指用函数调用的实参来初始化函数形参存储区的过程。
1、按值传递
把实参的值复制到形参的存储区中,即用实参值初始化形参。
int test(int left, int right){
return left + right;
}
int main(){
int lval = 2;
int rval = 3;
int result = test( lval , rval );
}
2、Const
使用const限定可以避免实参被修改
(三)函数重载
如果同一个作用域内的几个函数名字相同但形参列表不同,则它们是重载函数,调用重载函数时,编译器会根据实参的类型推断出要调用的是哪个函数。
void print(const int b, const int e){…}
void print(const int ia[], size_t size){…}
void print(const char cp){…}
int arr[5] = {1, 2, 3, 4, 5};
print(“Hello!”);
//调用print(const char )
print(arr, 5);
//调用print(const int, size_t)
print(begin(arr), end(arr));
//调用print(const int, const int)
(四)作用域和存储类别
1、对象的生存期:
是指程序执行过程中对象存在的时间,对象的生存期与对象的作用域和存储类别密切相关。
2、名字的作用域:
程序的一段区域,名字的作用域指的是该名字可以在哪些程序文本区使用。
3、对象的存储类别:
创建对象时分配内存空间的方式和内存空间的类型。C++程序中对象的存储类别有静态存储、自动存储、动态存储。
四、类与对象
对象是数据和操作的封装体;将数据和操作捆绑在一起,并加上访问控制,称为封装.数据描述对象的属性,操作描述对象的行为。
(一)访问控制和封装
信息和实现隐藏:
控制对类中不同成员的访问,对客户程序员隐藏实现信息,客户程序员只对类的接口编程,C++通过限定成员的访问权限来实现信息隐藏
(二)访问限定符
关键字public、private 和protected 被称为访问限定符
访问限定符在类定义中使用,一般语法为
struct 类名
{
public:
公有成员声明;
private:
私有成员声明;
protected:
被保护成员声明;
};
增加了成员访问限定的类,内部的安全性与一致性加强,降低了客户程序员操纵该类型的复杂程度,只使用public 部分提供的操作,不需了解类的内部表示,类设计者改变类的内部工作方式时客户程序不受影响。只要public 部分的声明没有改变,则不会影响之前使用该类的客户程序代码。
C++引入了一个关键字class 来定义类
class 和struct 定义的类稍有区别
如果class的成员没有设置访问限定符,则默认为private
(三)访问器和修改器
将数据成员限定为private,并提供public成员函数来对其进行访问,这种成员函数被称为访问器(accessor)和修改器(mutator)
常以一对getX()和setX() 函数的形式出现
一对重载函数的形式,如坐标点的x轴数据 int _x;
int x();
void x(int newX);
对bool成员,如电视机是否打开的数据bool powerOn;
bool on();
void turnOn(); void turnOff();
与直接访问数据成员相比,访问器和修改器更好地体现了封装性
可以在修改器中进行数据有效性的验证,从而确保对象不会因外部修改而处于无效的状态
(四)this指针
每个成员函数都有一个隐含的参数,指向接收消息的对象,称为this指针,X类的this指针的类型是X;this指针是一个常量,含有当前实施调用的对象的地址。不能改变this指针的值,也不能取this指针的地址。
五、对象的初始化、复制和销毁
(一)对象的初始化
1、默认初始化
如果定义对象时没有指定初值,对象被默认初始化,调用类中的默认构造函数。
2、直接初始化
使用等号“=”,容易和赋值混为一谈
赋值和初始化不同,是对已经存在的对象进行的,含义是清除对象当前的值,写入新的值来代替
3、拷贝初始化
用等号“=”初始化一个对象时,执行拷贝初始化,编译器用等号右边的初始值创建一个对象,复制给新创建的对象
等号右边的初始值只能有一个,调用与初始值类型匹配的构造函数
4、列表初始化
用花括号“{}”中的初始值构造对象,调用相应的构造函数,与直接初始化类似
花括号可以是初始值列表,用来初始化数组的每个元素
此时对每个值调用构造函数,创建数组元素
如果初始值的个数少于数组大小,对后面的元素调用默认构造函数初始化
(二)默认构造函数
可不提供实参就能调用的构造函数称为默认构造函数
例如,HaveAll()是HaveAll 类的默认构造函数
在定义对象时如果没有提供初始值,或不便提供初始值时,调用默认构造函数进行初始化
X ax[100]; 需要调用X()来初始化数组的每个元素
默认构造函数可以是没有形参的构造函数
(三)析构函数
对象离开作用域时析构函数会被自动调用
同一作用域的对象析构函数的调用次序和构造函数的调用次序相反
(四 )拷贝构造函数
X one;
X two(one); //用one初始化同类型对象two
用one 初始化two 时需要构造函数X(X&),称为拷贝构造函数
如果在类中没有定义这样的构造函数,编译器会自动合成一个,默认的行为是逐个成员复制
X two(one)就是用one 中的每个成员分别去初始化two 的每个对应的成员,这种行为也称为浅复制。
六、运算符重载
(一)基本概念
运算符重载:将运算符看作是一种特殊的函数,操作数是函数的参数,运算结果是函数的返回值。如果运算符被看作是函数,自然也可以像函数一样重载,已见过的运算符重载现象;移位运算符>>和<<,用作流输入和输出运算符。
重载的运算符:定义重载运算符和定义普通函数类似,函数名由关键字operator 和其后要定义的运算符组成。
例如:operator=, operator<<, operator+
返回类型:运算结果的类型
参数表:提供参与运算的操作数;参数个数取决于运算符的操作数个数和运算符函数是成员函数还是非成员函数。
函数体:进行运算,返回运算结果,即表达式的值。
(二)常用运算符的重载
用成员函数重载一元运算符
用非成员函数重载一元运算符
注意:对于类类型的参数,如果仅仅只是读参数的值,而不改变参数,应该作为const引用来传递。
普通算术运算符、关系运算符、逻辑运算符都不会改变参数,所以以const引用作为参数传递方式。
当运算符函数是类的成员函数时,就将其定义为const成员函数。
七、组合与继承
(一)组合
将一个类的对象作为另一个类的成员,被称作组合或包含。
class Engine{…};
class Wheel{…};
class Car{
Engine e;
Wheel wheels[4];
//…};
注意:
成员对象是组合对象的一部分,随着组合对象的创建而创建,随着组合对象的撤销而撤销。
成员对象不作为独立元素对外部展现。
(二)继承
继承:在已有类的基础上创建新类的过程,比方说:一个 B 类继承A类,或称从类 A 派生类 B。
被继承的已有类称为基类;继承得到的新类称为派生类;派生类可以再被继承,这样构成的层次结构称为继承层次。
语法举例:
某大学的学生管理程序中可能有如下的学生类student
class student {
string name;
int student_id;
string department;
public:
student(string nm, int id, string dp);
void print()const;
};
如果对研究生还要处理更多的信息,例如论文题目,student 类型就不能满足需要。
可以重新定义研究生类如下:
class grad_student {
string name;
int student_id;
string department;
string thesis;
public:
grad_student(string nm, int id, string dp, string th);
void print()const;
};
类继承关系的语法形式
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};
基类名表 构成
访问控制 基类名1, 访问控制 基类名2 ,… , 访问控制 基类名n
访问控制 表示派生类对基类的继承方式,使用关键字:
public 公有继承
private 私有继承
protected 保护继承
(三)重名人员
派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽了基类的同名成员。
在派生类中使用基类的同名成员,显式地使用类名限定符:
类名 :: 成员
void printAB()
{ A::print() ; //派生类对象调用基类版本同名成员函数
print() ; //派生类对象调用自身的成员函数
}
};
int main()
{ B b ; b.A::print(); b.printAB(); }
(四)派生类中访问静态成员
基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员),根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质。
派生类中访问静态成员,用以下形式显式说明:
类名 :: 成员
或通过对象访问 对象名 . 成员
(五)基类的初始化
在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据
派生类构造函数声明为
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )
… 对象成员n ( 变元表 ) ;
构造函数执行顺序:基类 对象成员 派生类
八、虚函数与多态
多态性(Polymorphism)是指一个名字,多种语义;或界面相同,多种实现。
重载函数是多态性的一种简单形式。
虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。
(一) 虚函数和动态联编
冠以关键字 virtual 的成员函数称为虚函数,实现运行时多态的关键首先是要说明虚函数,另外,必须用,基类指针调用派生类的不同实现版本。
例:
class base
{ public :
virtual void vf1 ( ) ;
virtual void vf2 ( ) ;
virtual void vf3 ( ) ;
void f ( ) ;
} ;
class derived : public base
{ public :
void vf1 ( ) ; // 虚函数
void vf2 ( int ) ; // 重载,参数不同,虚特性丢失
char vf3 ( ) ; // error,仅返回类型不同
void f ( ) ; // 非虚函数重载
} ;
(二)多态性
多态性(Polymorphism)是指一个名字,多种语义;或界面相同,多种实现。
重载函数是多态性的一种简单形式。
虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。
(三)虚析构函数
构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数
析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象。
#include
using namespace std ;
class A
{ public:
~A(){ cout << “A::~A() is called.\n” ; }
} ;
class B : public A
{ public:
~B(){ cout << “B::~B() is called.\n” ; }
} ;
int main() {
A *Ap = new B ;
B *Bp2 = new B ;
cout << “delete first object:\n” ;
delete Ap;
cout << “delete second object:\n” ;
delete Bp2 ;
}
学习感想:
面向对象的内容很复杂,不再是简单的语法,一些知识也很抽象,在学习过程中,出现了几个问题。
一、不清楚语法含义
比方说类和对象的含义我就想了很长时间,不明白这种概念真正的含义,没有合适的举例,会造成使用过程混乱。
二、细节过多
Stl的细节很多,尤其是那些标准库类型的函数,一开始比较常用的几个还是翻书才查到的,如果对一些细节不够熟悉会让使用很困难,并且也很难举一反三。
总归来说,经过这一年的学习,算是知道了c++语言的皮毛,有很大收获。