a[]表示访问数组元素,比如int a[10]定义了长度为10的整型数组,标号从0-9,a[3]就表示下标为3的元素(第四个)
数据类型 数组名 = new 数据类型 [数组长度]。
float *p = new float [5];
在C++ 中,变量的三个基本要素是指:
变量名、变量类型、变量值。
初始化:
静态成员变量可以初始化,但只能在类体外进行初始化。如:
long long student::number = 13926572996; //在类体外对静态成员变量赋值
其一般形式为:
数据类型 类名::静态成员变量名 = 初值;
生存周期分为“静态生存周期”与“动态生存周期”。
静态生存期的变量有
(1)全局变量
(2)静态局部变量
(3)静态成员变量
从定义开始直到本程序结束才被销毁
特点:
(1)静态成员函数没有this指针,正因为他没有this指针,他才可以不通过对象来对静态成员变量进行访问。
(2)静态成员函数中,无法对本类的非静态成员进行默认访问
动态生存周期
对象或者变量从定义开始,到其所在函数或者语句块结束时被销毁
例如: i从定义开始,到for循环结束而消失
重载类PrintInfo
(ostream &, const PrintInfo & )
使用对象的引用来初始化创建中的对象的函数是 拷贝构造函数 。
复制构造函数使用 对象的引用 作为形式参数。
常量的值不可以修改,任何尝试修改常量的操作都会导致编译错误。而变量可以通过赋值来改变。
常量定义后就不可修改,所以常量在定义时就必须初始化。变量可以定义时暂时不进行初始化。常量初始化的时候必须直接赋值。
局部变量没有赋初值时,其值是不确定的。
全局变量没有赋处置时,默认为0
C++ 提供预处理的语句有三种
文件包含、条件编译和宏定义
静态联编:编译阶段就将函数实现与函数调用关联起来;
动态联编:在程序执行阶段才将函数实现和调用关联;
class C:public A,public B
构造函数执行顺序 A,B,C
析构函数执行顺序 C,B,A
构造函数不应该有void
“封装” 要求有一个对象应具备明确的功能,并具有接口以便和其他对象相互作用。
公有继承为公有
私有继承为私有
派生类也是封闭类的情况下,
生成派生类对象时,根据派生层次从上至下依次执行所有基类的构造函数。
派生类从基类保护继承时,基类中的私有成员在派生类中是隐藏的,不能直接访问。
继承性允许派生类继承基类的部分成员,并允许增加新的成员或重定义基类的成员。
通过对象访问成员变量的一般格式为 “ 对象名.成员变量名 ”
指针类型的强制转换格式:(char *)p
运算符重载为成员函数时,若参数表中无参数,重载的是一元运算符。
二元运算符只需要传递一个参数。
运算符重载格式
案例:
class Person {
public:
//成员函数重载+号,本质调用为Person p3 = p1.operator+(p2);
Person operator+(Person& p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_A + p.m_B;
return temp;
}
int m_A;
int m_B;
};
#include
using namespace std;
class Person {
public:
//通常,我们不会利用成员函数重载左移运算符,因为无法将cout在左侧实现,调用时得写成p<
int m_A;
int m_B;
};
//利用全局函数重载左移运算符
ostream& operator<<(ostream& cout, Person& p)
{
cout << "p.m_A= " << p.m_A << endl;
cout << "p.m_B= " << p.m_B << endl;
return cout;
}
int main()
{
Person p;
p.m_A = 10;
p.m_B = 10;
cout << p << endl;
return 0;
}
模板类重载左移运算符
#include
#include
using namespace std;
template <class T1,class T2>
class Pair
{
public:
T1 key; //关键字
T2 value; //值
Pair(T1 k,T2 v):key(k),value(v) { };
bool operator < (const Pair<T1,T2> & p) const;
bool operator > (Pair<T1,T2> & p){
cout << "key:"<< key << endl;
cout << "p.key:" <<p.key << endl;
return key > p.key;
};
friend ostream& operator<<(ostream& cout, Pair<T1,T2>& p)
{
cout << endl << "p.key = " << p.key << endl;
return cout;
};
Pair operator+(Pair<T1,T2>& p)
{
Pair<string,int> temp("",0);
temp.value = value + p.value;
return temp;
};
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
//Pair的成员函数 operator <
{ //"小"的意思就是关键字小
return key < p.key;
}
int main()
{
Pair<string,int> student("Tom",20); //实例化出一个类 Pair
cout << student.key << " " << student.value << endl;
Pair<string,int> student1("Zack",18);
cout << student1.key << endl;
cout << "--"<< endl;
if(student1 > student) cout << "1";
if(student < student1) cout << "2";
cout << student << endl;
Pair<string,int> student3("sum",0);
student3 = student + student1;
cout << student3.value << endl;
return 0;
}
#include
using namespace std;
class MyInteger
{
public:
MyInteger()
{
m_Num = 10;
}
//重载前置++运算符(++i)
MyInteger & operator++()
{
++m_Num;
return *this; //将类本身做返回
}
//重载后置++运算符(i++)
//因为函数返回的是局部变量temp,当函数调用完后会释放局部变量,所以函数只能返回值,而不是引用&
MyInteger operator++(int) //int 代表占位参数,用于区分前置和后置递增
{
MyInteger temp = *this;
m_Num++;
return temp; //将类本身做返回
}
int m_Num;
};
ostream &operator<<(ostream &cout, MyInteger &myint)
{
cout << myint.m_Num;
return cout;
}
int main()
{
MyInteger myint;
cout << ++myint << endl;
return 0;
}
(1).重载前置运算符不需要传入参数,而重载后置运算符需要传入一个占位参数int;
(2).重载前置运算符返回的是引用,后续可以对该值继续链式调用; 重载后置运算符直接返回局部变量的值,因为局部变量在函数调用完后就被释放,所以如果引用一个局部变量地址就会报错。
#include
using namespace std;
class Person {
public:
int *m_Age;
Person(int age)
{
m_Age = new int(age);
}
~Person() //写出析构函数是为了方便看出浅拷贝和深拷贝的区别
{
if (m_Age) {
delete m_Age;
m_Age = NULL;
}
}
Person &operator=(Person& p)
{
//先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if (m_Age)
{
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(* p.m_Age);
//编译器只提供如下浅拷贝:
//m_Age = p.m_Age;
return *this;
}
};
void test()
{
Person p1(10);
Person p2(20);
p2 = p1; //赋值操作,调用重载函数
cout << "p1.m_Age= " << *p1.m_Age << endl;
cout << "p2.m_Age= " << *p2.m_Age << endl;
}
int main()
{
test();
return 0;
}
bool operator==(Person& p)
{}
bool operator < (const Pair<T1,T2> & p) const;
类外定义
template<class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
//Pair的成员函数 operator <
{ //"小"的意思就是关键字小
return key < p.key;
}
#include
using namespace std;
class MyPrint
{
public:
void operator()(string str)
{
cout << str << endl;
}
};
int main()
{
MyPrint myprint;
myprint("hello world!"); //这里的()使用了重载函数,其本身也像一个函数
return 0;
}
Class *p
抽象类定义:如果一个类中至少有一个纯虚函数,那么这个类就被称为抽象类。
派生类中必须重载基类中的纯虚函数,否则它仍然是一个抽象类。
从基类继承类的纯虚函数,在派生类中仍然是虚函数
抽象类的一个重要特点:抽象类只能用作派生类的基类,而不能直接创建对象实例。
原因:其中一个或多个函数没有定义,但仍可使用指向抽象类的指针支持运行时多态性。
类模板的重点是模板。表示的是一个模板,专门用于产生类的模子。例子:
类模板
类模板中的成员函数全部都是模板函数。
加参数型:
template<class NameType,class AgeType=int>
class Teacher
{
Teacher(NameType name,AgeType age){
this->m_Name = name;
this ->m_Age = age;
}
NameType m_Name;
AgeType m_Age;
}
使用类模板创建对象时,要随类模板名给出对应于类型形参或普通形参的具体实参,可以使用格式
模板类
Teacher<string, int> t2("xiansifan", 22);
//或者
Teacher<string> t1("xiansifan", 234);
//类模板在模板参数列表中可以有默认参数
深拷贝,手动定义 Rect 类中的拷贝函数,默认定义的 Rect 类为浅拷贝。
Rect(const Rect& r)
{
width = r.width;
height = r.height;
p = new int; // 为新对象重新动态分配空间
*p = *(r.p);
}
iomanip
setbase(n)
setfill(n)
setprecision(n)
setw(n)
setiosflags(ios::fixed)
setiosflags(ios::scientific)
setiosflags(ios::left)
setiosflags(ios::right)
setiosflags(ios::skipws)
友元运算符@obj 被 C++ 编译器解释为
operator@(obj)
friend void Student::score();
在基类的实现纯虚函数的方法是在函数原型后添加=0
(virtual void funtion1()=0)
一般的讲,如果类中定义有虚函数,则其析构函数也应该声明为虚函数,特别是在析构函数中需要完成一些有意义的操作,比如释放内存时
当编译系统编译含有虚函数的类时,将为它建立一个虚函数表,表中的每一个元素都指向一个虚函数的地址。
put输出ASCII转字符
例如char c = ‘a’
cout put(c+25)
结果:z
面向对象程序设计能进行功能抽象
面向对象程序设计能进行数据抽象
头文件,创建对象,打开文件,对数据操作,关闭文件
文件类型分为两种:
文本文件:文本文件的ASCII码形式存储在计算机中
二进制文件:文本文件的 二进制 形式存储在计算机中,用户一般不能直接读懂它们
ofstream:写操作
ifstream:读操作
fstream:读写操作
#include
ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就是内存空间;
fstream有两个子类:ifstream(input file stream)和ofstream(outpu file stream),ifstream默认以输入方式(in)打开文件,而ofstream默认以输出方式(out)打开文件。
ofstream ofs; //写文件
ifstream ifs; //读文件
ofs.open(“文件路径”,打开方式);
ofs<<“写入的数据”;
ifs<<变量名/字符串; //读数据
ofs.close();
ios::in : 为读文件而打开文件
ios::out : 为写文件而打开文件
ios::app : 初始位置:文件尾
ios::app : 追加方式写文件
ios::trunc : 如果文件存在先删除,在创建
ios::binary : 二进制方式
注意,文件打开方式可以配合使用,利用 | 操作符
例如:用二进制方式写文件 ios::out | ios::binary
std::fstream ofs(“F:\1.txt”, std::ios::in | std::ios::out);
写文件
#include
#include
using namespace std;
#include //头文件包含
//文本文件 - 写文件
void text01()
{
//1.包含头文件 fstream
//2.创建流对象
ofstream ofs; // o - 写 f - 文件 stream - 流
//3.指定打开方式
ofs.open("text.txt", ios::out);
//4.写内容
ofs << " 姓名:张三" << endl;
ofs << " 年龄:19 " << endl;
ofs << " 性别:男 " << endl;
//5.关闭文件
ofs.close();
}
int main()
{
text01();
return 0;
}
读文件
void text02()
{
//1.包含头文件 fstream
//2.创建流对象
ifstream ifs; //ifstream ifs("Person.txt", ios::out | ios::binary);
//3.打开文件 并且判断是否打开成功
ifs.open("text.txt", ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
//4.读内容
for (int i = 0; i < 5 ; i++ ){
char str[100] = { 0 };
ifs.getline(str, 100);
std::cout << str << std::endl;
}
//5.关闭文件
ifs.close();
}
判断文件行数
#include
int main()
{
FILE * fp = NULL; //文件指针。
int c, lc=0; //c为文件当前字符,lc为上一个字符,供结尾判断用。
int line = 0; //行数统计
fp = fopen("text.txt", "r");//以只读方式打开文件。
if(fp == NULL) return -1; // 文件打开失败。
while((c = fgetc(fp)) != EOF) //逐个读入字符直到文件结尾
{
if(c == '\n') line ++; //统计行数。
lc = c; //保存上一字符。
}
fclose(fp); //关闭文件
if(lc != '\n') line ++;//处理末行
printf("文件共有%d行。\n", line);
return 0;
}