找到
29
篇与
刘纪彤
相关的结果
- 第 5 页
-
文件 文件 C++可以将进行文件操作。C++使用文件的时候需要包含#include<fstream>这个库。 文件类型分为两种,一种是二进制文件,一种是文本文件。他们分别对应两大种存储方式。一个我们可以通过类似于记事本方式打开,一个一般情况之下我们看不太懂。 文本文件 包含头文件 #include<fstream> 创建流对象 ofstream ofs; 打开文件 ofs.open("文件路径",打开方式); 写数据 ofs << "写入的数据"; 关闭文件 ofs.close(); 文件打开方式: 打开方式解释ios::in为读文件而打开文件ios::out为写文件而打开文件ios::ate初始位置:文件尾ios::app追加方式写文件ios::trunc如果文件存在先删除,再创建ios::binary二进制方式注意: 文件打开方式可以配合使用,利用|操作符 例如:用二进制方式写文件 ios::binary | ios:: out for example: //文件-1(文本文件写入) #include<iostream> #include<fstream> using namespace std; int main() { ofstream ofs1; ofs1.open("1.txt",ios::out); ofs1<<"我是你的爸爸"<<endl; ofs1<<"66666"<<endl; ofs1.close(); }读文件其实类似,只需要把输出流fstream改为输入流ifstream就可以了,头文件不变。看代码 //文件-2(文本文件读取) #include<iostream> #include<fstream> using namespace std; int main() { ifstream ifs1; ifs1.open("1.txt",ios::in); if (!ifs1.is_open()) { cout << "文件打开失败" << endl; return 0; } //第一种方式 //char buf[1024] = { 0 }; //while (ifs >> buf) //{ // cout << buf << endl; //} //第二种 //char buf[1024] = { 0 }; //while (ifs1.getline(buf,sizeof(buf))) //{ // cout << buf << endl; //} //第三种 //string buf; //while (getline(ifs1, buf)) //{ // cout << buf << endl; //} char c; while ((c = ifs1.get()) != EOF)//EOF:end of file { cout << c; } ifs1.close(); }二进制文件 二进制文件的读取和写入,可以用以下两个函数原型: 写入:ostream& write(const char * buffer,int len);参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数 读取:istream& read(char *buffer,int len);参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数 同时加上|操作符,ios::binary以二进制打开就可以了 for example
-
内存分区 内存分区 C++内存分区模型 C++的程序在执行过程中,大体会划分为四个区域,也就是:代码区,全局区,堆区,栈区。 在程序进行了编译之后C++的编译器会在编译之后,生成一个.exe的可执行文件,这个可执行文件在被执行之前,会有两个区域。 代码区: 存放 CPU 执行的机器指令 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令 全局区: 全局变量和静态变量存放在此. 全局区还包含了常量区, 字符串常量和其他常量也存放在此. ==该区域的数据在程序结束后由操作系统释放==. 可以利用下面这行代码理解一下: //全局变量 int g_a = 10; int g_b = 10; //全局常量 const int c_g_a = 10; const int c_g_b = 10; int main() { //局部变量 int a = 10; int b = 10; //打印地址 cout << "局部变量a地址为: " << (int)&a << endl; cout << "局部变量b地址为: " << (int)&b << endl; cout << "全局变量g_a地址为: " << (int)&g_a << endl; cout << "全局变量g_b地址为: " << (int)&g_b << endl; //静态变量 static int s_a = 10; static int s_b = 10; cout << "静态变量s_a地址为: " << (int)&s_a << endl; cout << "静态变量s_b地址为: " << (int)&s_b << endl; cout << "字符串常量地址为: " << (int)&"hello world" << endl; cout << "字符串常量地址为: " << (int)&"hello world1" << endl; cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl; cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl; const int c_l_a = 10; const int c_l_b = 10; cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl; cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl; system("pause"); return 0; }局部变量是不会存放在全局区中,全局区中又会包含常量,全局常量和字符串常量。无论如何局部的常量或者变量都不会存放在全局区中。 当然在程序执行后,又会有两个区域。 栈区: 由编译器自动分配释放, 存放函数的参数值,局部变量等 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放 堆区: 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收 在C++中主要利用new在堆区开辟内存 new操作符 基本语法:new 数据类型 new返回的是该数据对性的类型的指针。堆区的数据由代码的编写者管理开辟,管理释放,释放需要用到delete。 释放数组的时候要加一个[] 举例: new int a[10];//堆区创建一个数组 new int a(10);//堆区创建一个整型值为10 delete p;//释放 delete[] p;//释放数组
-
模板 模板 模板是一类通用的模型,无论什么情况我们都有可以调用他,提高复用性。 C++中的模板分为函数模板和类模板两大类型。 函数模板 作用:建立一个通用的函数,其函数返回值类型和形参不具体指向特定的数据类型,用一个虚拟的类型来表示 语法: template<typename T> 函数声明很容易template表示这是一个模板,typename后面的T是一个数据类型,可以用class代替。T就是一个变量 在使用过程中可以系统可以自己推导出来数据类型且必须一致才可以使用,同时也可以自己指定数据类型。 //模板-1 #include<iostream> using namespace std; //利用模板提供通用的交换函数 template<class T> void mySwap(T& a, T& b) { cout<<"mySwap的调用"<<endl; T temp = a; a = b; b = temp; } // 1、自动类型推导,必须推导出一致的数据类型T,才可以使用 void test01() { int a = 10; int b = 20; char c = 'c'; c++; mySwap(a, b); // 正确,可以推导出一致的T //mySwap(a, c); // 错误,推导不出一致的T类型 } // 2、模板必须要确定出T的数据类型,才可以使用 template<class T> void func() { cout << "func 调用" << endl; } void test02() { //func(); //错误,模板不能独立使用,必须确定出T的类型 func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板 } int main() { test01(); test02(); return 0; }结果: mySwap的调用 func 调用不过函数模板并不是万能的,还是有它的局限性的。C++还可以对模板重载和对特定的数据类型进行具体化。 譬如说: //具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型 //具体化优先于常规模板 template<> bool myCompare(Person &p1, Person &p2)上面就是一个简单的例子,他对bool类型进行了一个特殊的定义。 类模板 语法同函数模板,类模板没有自动类型推导的使用方式也就是说,你必须使用指定类型 例如: Peson<string,int>p("刘纪彤",1000);值得一提的是类模板中的模板参数列表可以有默认参数。 template<class NameType, class AgeType = int> 像这样AgeType就默认是int,后面可以直接用默认的int类型。大体和函数那里互相类似。 类模板中的成员函数并不是一开始就创建的,在调用时才去创建 //模板-2 #include<iostream> using namespace std; class Person1 { public: void showPerson1() { cout << "Person1 show" << endl; } }; class Person2 { public: void showPerson2() { cout << "Person2 show" << endl; } }; template<class T> class MyClass { public: T obj; //类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成 void fun1() { obj.showPerson1(); } void fun2() { obj.showPerson2(); } }; void test01() { MyClass<Person1> m; m.fun1(); //m.fun2();//error: 'class Person1' has no member named 'showPerson2'; } int main() { test01(); return 0; }结果: Person1 show传参数的时候可以有三种情况一共有三种传入方式: 指定传入的类型 --- 直接显示对象的数据类型 参数模板化 --- 将对象中的参数变为模板进行传递 整个类模板化 --- 将这个对象类型 模板化进行传递 //1、指定传入的类型 void printPerson1(Person<string, int> &p) //2、参数模板化 template <class T1, class T2> void printPerson2(Person<T1, T2>&p) //3、整个类模板化 template<class T> void printPerson3(T & p)模板的继承: 当类模板碰到继承时,需要注意一下几点: 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型 如果不指定,编译器无法给子类分配内存 如果想灵活指定出父类中T的类型,子类也需变为类模板 示例 class Son :public Base<int> //必须指定一个类型 //类模板继承类模板 ,可以用T2指定父类中的T类型 template<class T1, class T2> class Son2 :public Base<T2>类外实现就可以用通过::实现,示例代码如下: template<class T1, class T2> void Person<T1, T2>::showPerson() {} template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) {}类模板实现友元函数: 全局函数类内实现 - 直接在类内声明友元即可 全局函数类外实现 - 需要提前让编译器知道全局函数的存在
-
类和对象(多态) 类和对象(多态) 多态 多条是C++面向对象的三大特性之一 多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指针或引用调用成员函数,执行不同的函数。 多态的分类 多态分为两种:编译时多态和运行时多态。 编译时多态:函数重载和运算符重载属于编译时多态,因为函数重载和运算符重载是静态的,函数地址早绑定。 运行时多态:虚函数和抽象类属于运行时多态,因为虚函数和抽象类是动态的,函数地址晚绑定。 案例: //多态-1 #include <iostream> using namespace std; class Base { public: void func() { cout<<"Base func"<<endl; } }; class Derived : public Base { void func() { cout << "Derived func()" << endl; } }; int main() { Base *p = new Derived(); p->func(); return 0; }运行结果: Base func()说明并未发生重写,而是发生了隐藏,因为在编译时,编译器会根据指针的类型来决定调用哪个函数,而不是根据指针所指向的对象的类型来决定调用哪个函数。 但是,如果将函数声明为虚函数,就可以实现运行时多态,如下所示: //多态-2 #include <iostream> using namespace std; class Base { public: virtual void func() { cout << "Base func()" << endl; } }; class Derived : public Base { public: void func() { cout << "Derived func()" << endl; } }; int main() { Base *q= new Base(); Base *p = new Derived(); Derived *d = new Derived(); q->func(); p->func(); d->func(); return 0; }Base func() Derived func() Derived func()显然的,这里的多态是运行时多态,因为在运行时,编译器会根据指针所指向的对象的类型来决定调用哪个函数。 总结: 多态满足条件 有继承关系 子类重写父类中的虚函数 多态使用条件 父类指针或引用指向子类对象 相关概念:重写:函数返回值类型 函数名 参数列表 完全一致称为重写 多态的实现 多态的实现有两种方式:虚函数和抽象类。 虚函数:虚函数是在基类中使用关键字virtual声明的函数,虚函数的特点是在运行时才动态绑定,可以实现运行时多态。 抽象类:抽象类是包含纯虚函数的类,抽象类的特点是不能实例化对象,只能作为接口使用。 多态的好处 多态的好处是提高了代码的扩展性和复用性。 提高了代码的扩展性:通过多态,可以在不修改原有代码的基础上,对原有代码进行扩展,增加新的功能。 提高了代码的复用性:通过多态,可以在不修改原有代码的基础上,对原有代码进行复用,增加新的功能。 多态的弊端 多态的弊端是降低了代码的可读性和可维护性。 降低了代码的可读性:通过多态,可以在不修改原有代码的基础上,对原有代码进行扩展,增加新的功能。 降低了代码的可维护性:通过多态,可以在不修改原有代码的基础上,对原有代码进行复用,增加新的功能。 虚函数 虚函数是在基类中使用关键字virtual声明的函数,虚函数的特点是在运行时才动态绑定,可以实现运行时多态。 虚函数的声明 虚函数的声明格式如下: virtual 返回值类型 函数名(参数列表);纯虚函数和抽象类 纯虚函数是在基类中使用关键字virtual声明的函数,并在函数后面加上=0,纯虚函数的特点是没有函数体,只有函数原型,不能被调用,只能被重写。 抽象类是包含纯虚函数的类,抽象类的特点是不能实例化对象,只能作为接口使用。 纯虚函数和抽象类的声明格式如下: class 类名 { public: virtual 返回值类型 函数名(参数列表) = 0; };只要有纯虚函数的类,就是抽象类,抽象类不能实例化对象,只能作为接口使用。 使用时注意: 抽象类无法实例化对象 子类必须重写父类中的纯虚函数,否则也属于抽象类 示例: //多态-3 #include <iostream> using namespace std; class Base { public: virtual void func() = 0; }; class Derived : public Base { public: void func() { cout << "Derived func()" << endl; } }; class Derived2 : public Base { public: void func() { cout << "Derived2 func()" << endl; } }; int main() { Base *p = new Derived(); Derived2 d2; p->func(); d2.func(); return 0; }Derived func() Derived2 func()子类重写父类中的虚函数,就可以实现运行时多态。 虚析构函数和纯虚析构函数 虚析构函数是在基类中使用关键字virtual声明的析构函数,虚析构函数的特点是在运行时才动态绑定,可以实现运行时多态。 纯虚析构函数是在基类中使用关键字virtual声明的析构函数,并在函数后面加上=0,纯虚析构函数的特点是没有函数体,只有函数原型,不能被调用,只能被重写。 虚析构函数和纯虚析构函数的声明格式如下: virtual ~类名();virtual ~类名() = 0;使用纯虚析构的好处是可以防止内存泄漏,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。所以需要虚析构函数或者纯虚析构函数。 使用纯虚析构函数的时候同样不能实例化对象,只能作为接口使用。 虚析构示例: //多态-4 #include <iostream> using namespace std; class Base { public: virtual ~Base() { cout << "Base析构函数" << endl; } }; class Derived : public Base { public: ~Derived() { cout << "Derived析构函数" << endl; } }; void test01() { Base *p = new Derived(); delete p; } int main() { test01(); return 0; }Derived析构函数 Base析构函数纯虚析构示例: //多态-5 #include <iostream> using namespace std; class Base { public: virtual ~Base() = 0; }; Base::~Base() { cout << "Base析构函数" << endl; } class Derived : public Base { public: int *p; Derived() { p = new int(10); } ~Derived() { cout << "Derived析构函数" << endl; //释放堆区数据 if (p != NULL) { delete p; p = NULL; } } }; void test01() { Base *p = new Derived(); delete p; } int main() { test01(); return 0; }Derived析构函数 Base析构函数
-
类和对象(继承) 类和对象(继承) 继承 C++面向对象具有三大特点:封装、继承、多态。 不难发现的是,有些类的成员变量和成员函数与其他类的成员变量和成员函数有很多相似的地方,这时候就可以使用继承来减少代码量。 例如,马和驴同属于动物,他们呢又可以生出来骡子,具有马和驴的属性。 继承的基本写法: class 子类名:继承方式 父类名 { //子类的成员变量和成员函数 }继承方式有三种: 私有继承,公共继承和保护继承。 分为以下三种情况(内容代表在子类中的访问权限): 继承方式父类的private父类的public父类的protected私有继承不可访问privateprivate公共继承不可访问publicprotected保护继承不可访问protectedprotected注意:父类中的private成员变量和成员函数在子类中都是不可访问的。但是private会被继承下去 私有继承 私有继承的写法: class 子类名:private 父类名 { //子类的成员变量和成员函数 }私有继承的特点是:子类中的成员变量和成员函数不能直接访问父类的成员变量和成员函数,但是可以通过父类的公共成员函数来访问。 例如: //3-9 #include<iostream> using namespace std; class example1 { public: int a=1; protected: int b=2; private: int c=3; }; class example2 :private example1 { public: void print() { cout<<a<<endl; cout<<b<<endl; //cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的 } }; int main() { example2 ex; //cout<<ex.a<<endl;这显然是不可以的,因为私有继承,这个访问权限是private的 //cout<<ex.b<<endl;同理,这个访问权限也是private的 ex.print(); }所以当私有继承的时候,子类中的成员函数不能直接访问父类的成员变量和成员函数,但是可以通过父类的公共成员函数来访问。 公共继承 公共继承的写法: class 子类名:public 父类名 { //子类的成员变量和成员函数 }公共继承的特点是:子类中的成员变量和成员函数可以直接访问父类的成员变量和成员函数。 就不举例子了,和父类的所有访问权限都相同(仅限于public和protected) 保护继承 保护继承的写法: class 子类名:protected 父类名 { //子类的成员变量和成员函数 }保护继承的特点是:子类中的成员变量和成员函数可以直接访问父类的成员变量和成员函数,但是不能通过子类的对象来访问。 例如: //3-10 #include<iostream> using namespace std; class example1 { public: int a=1; protected: int b=2; private: int c=3; }; class example2 :protected example1 { public: void print() { cout<<a<<endl; cout<<b<<endl; //cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的 } }; class example3 :protected example2 { public: void print() { cout<<a<<endl; cout<<b<<endl; //cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的 } }; int main() { example2 ex; //cout<<ex.a<<endl;//这显然是不可以的,因为私有继承,这个访问权限是protected的 //cout<<ex.b<<endl;//同理,这个访问权限也是protected的 //如example3 所示,protected继承,子类中的成员函数可以访问父类中的protected成员变量和成员函数 ex.print(); example3 ex2; ex2.print();//example3也可以访问到a,b说明protected继承的子类访问权限是protected }注意:private权限虽然不可访问,但是会被继承下去。 可以采用visual studio的命令行参数来查看类的内存布局,例如: cl /d1 reportSingleClassLayout类名 文件名继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反 同名处理: 子类对象可以直接访问到子类中同名成员 子类对象加作用域可以访问到父类同名成员 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数 静态成员和非静态成员出现同名,处理方式一致。 规范命名。 多继承 多继承的写法: class 子类名:继承方式1 父类名1,继承方式2 父类名2 { //子类的成员变量和成员函数 }多继承可能会引发父类中有同名成员出现,需要加作用域区分 C++实际开发中不建议用多继承 虚继承 虚继承的写法: class 子类名:virtual 继承方式 父类名 { //子类的成员变量和成员函数 }虚继承的特点是:解决多继承中父类中有同名成员出现的问题。 例如: //3-11 #include<iostream> using namespace std; class example1 { public: int a=1; protected: int b=2; private: int c=3; }; class example2 :virtual public example1 { public: void print() { cout<<a<<endl; cout<<b<<endl; //cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的 } }; class example3 :virtual public example1 { public: void print() { cout<<a<<endl; cout<<b<<endl; //cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的 } }; class example4 :public example2,public example3 { public: void print() { cout<<a<<endl; cout<<b<<endl; //cout<<c<<endl;//父类中的private成员变量和成员函数在子类中都是不可访问的 } }; int main() { example4 ex; ex.print(); }可以看到,example4中的a,b都是1,2,说明虚继承解决了多继承中父类中有同名成员出现的问题。 虚继承可以解决菱形继承的问题,菱形继承的代码如下: #include<iostream> using namespace std; class example { public: int m_Age=100; }; class example2 :virtual public example { public: int m_Age=102; }; class example3:virtual public example { int m_age=103; }; class example4:virtual public example2,virtual public example3 { public: void print() { cout<<example::m_Age<<endl; cout<<example2::m_Age<<endl; cout<<example3::m_Age<<endl; cout<<example4::m_Age<<endl; } }; int main() { example4 e; e.print(); return 0; }100 102 100 102这个代码在visual studio中的对象布局如下: