当前位置:   article > 正文

C++复习_"下列程序代码段fstream myin;myin.open(\"data.txt\", ios::

"下列程序代码段fstream myin;myin.open(\"data.txt\", ios::out | ios::app);"

内存四区
代码区:存放函数体的二进制代码,由操作系统进行管理,共享,只读
全局区:存放全局变量,const全局常量,""字符串常量,static静态变量
栈区:编译器自动分配释放,存放函数参数值,局部变量,const局部常量
堆区:程序员进行分配释放new/delete,程序结束时操作系统会回收
    元素:int* x=new int(6);delete x;
    数组:int* x=new int[6];delete[] x;

引用
初始化:int &b = a;之后不能变
本质:int* const b=&a;后面用b时就改写成*b
特别:const int& ref=10;即int temp=10;const int& ref=temp;

函数默认参数
int func2(int a,int b=10){return a + b;}
有传入就用传入,无传入就用默认
某位置有默认,右边参数都要有默认
声明与定义,只能有一个有默认参数

函数占位参数
void func(int a ,int){;}或void func(int a ,int=10){;}

函数重载
同一作用域下函数名称相同,参数类型/个数/顺序不完全相同,返回值无要求
不要出现二义性void func(int a,int b=1){}和void func(int a){}
func(a)调用void func(int &a){},func(10)调用void func(const int &a){}

封装权限
公共权限public:类内可以访问,类外可以访问  
保护权限protected:类内可以访问,类外不可以访问,儿子可以访问父亲中的保护内容
私有权限private:类内可以访问,类外不可以访问,儿子不可以访问父亲的私有内容
注意struct和class区别:struct默认权限是公共public,class默认权限是私有private
成员属性设为私有可以自己控制读写权限即get/set函数,检测有效性

构造与析构
构造函数:无返回值亦无void,函数名是类名,可带参可重载,创建对象时自动调用
Person p1;调用普通无参构造Person(){},注意Person p1()是函数声明不能调用
Person p2(10);或Person p2=Person(10);或Person p2=10;调用普通有参构造Person(int a){}
Person p3(p2);或Person p3=Person(p2);或Person p3=p2;调用拷贝构造Person(const Person &p){}
注意Person(10)与Person(p2)叫匿名对象,所在代码行执行完后即销毁,第3种隐式执行时换成第2种显式
析构函数:无返回值亦无void,函数名是~类名,无参不可重载,销毁对象时自动调用
拷贝函数调用时机归纳:函数值传参与返回,

构造函数调用规则
C++给每个类至少添加3个函数:默认无参构造,默认拷贝构造,默认析构
关于构造:用户写了有参构造,编译器仍提供默认构造;用户写了拷贝构造,编译器不再提供

深拷贝:(不写就是默认浅拷贝age=tage;指向同一个堆地址)
class Person{
public: Person(int tage){age=new int(tage);}
        Person(const Person& p){age=new int(*p.age);}
        ~Person(){if(age!=NULL){delete age;age=NULL;}
    Person& operator=(const Person &temp){*(this->age)=*(temp.age);return *this;}}
private:int* age;
};

初始化列表
初始化传统操作Person(int a,int b,int c){m_A=a;m_B=b;m_C=c;}
初始化列表操作Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){}

当A类成员中有B类对象,构造A类对象时先构造B类对象,析构则相反

静态成员变量(static修饰)
1、所有对象都共享同一份数据
2、编译阶段就分配内存
3、类内声明,类外初始化操作(包括private属性成员)
class Person{private:static int age;};
int Person::age = 200;
4、访问方式Person::age或p.age

静态成员函数
1、所有对象共享同一个函数
2、静态成员函数只能访问静态成员变量

类对象的所占空间大小
class Person{
    int m_A; // 非静态成员变量  属于类的对象上
    static int m_B; //静态成员变量  不属于类对象上
    void func() {} //非静态成员函数 不属于类对象上
    static void func2() {} //静态成员函数 不属于类的对象上
};
Person p;cout<<sizeof(p)<<endl;有int m_A;时是4B,否则是1B
C++编译器给每个空对象分配1B,以区分空对象占内存的位置

this指针
1、不需要定义,隐含的指向被调用成员函数所属对象
2、当形参与成员变量同名时用this区分(可人为避免同名)
3、类非静态成员函数返回对象本身时用return *this(链式编程)
4、本质是const Person * const this;

空指针调用成员函数
void showName(){if(this == NULL)return;cout<<this->name<<endl;}
Person *p = NULL;p->showName();

常函数:
1、定义时在成员函数后加const,如void getName() const{}
2、常函数内不可修改成员属性,但有例外(见第3点)
3、成员属性声明时加关键字mutable后常函数中依然可修改

常对象:
1、声明对象前加const称该对象为常对象,如const Person p; 
2、常对象只能调用常函数,如p.getName();

友元
第一种:全局函数做友元
class Person{
friend int getAge(Person *p);
private:int age=10;
};
int getAge(Person *p){return p->age;}
第二种:类做友元(下面GoodGay可以访问Building的private成员)
class Building;//一、先声明以防报错
class GoodGay{
public:
    GoodGay();//二、函数在外面写
    void visit();
    Building * building;//三、有指针
};
class Building{
    friend class GoodGay;//四、声明友元
public:Building(){m_BedRoom="卧室";}
private:string m_BedRoom;
};
GoodGay::GoodGay(){building=new Building;}
void GoodGay::visit(){cout<<building->m_BedRoom<<endl;}
第三种:成员函数做友元
上例中friend class GoodGay;改成friend void GoodGay::visit();

运算符重载
一、加号
class Cal{
public:
   Cal operator+(const Cal &c){
        Cal temp;
        temp.a=this->a+c.a;
        temp.b=this->b+c.b;
        return temp;
    }
    int a=10;
    int b=20;
};
Cal operator+(const Cal &x,const Cal &y){//y可改成int重载
    Cal temp;
    temp.a=x.a+y.a;
    temp.b=x.b+y.b;
    return temp;
}
经验证,全局重载优先级高于成员函数重载
二、左移运算符++
class Cal{
public:int a=10,b=10;
    friend ostream& operator<<(const ostream &out,const Cal &c);
};
ostream& operator<<(ostream &out,const Cal &c){return out<<c.a<<' '<<c.b;}
三、自增++
class Cal{
friend ostream& operator<<(ostream& out,const Cal &x);
public:    Cal& operator++(){this->a++;return *this;}//++x
    Cal operator++(int){Cal temp = *this;a++;return temp;}//x++,不可连续(x++)++
private:int a=10;
};
ostream& operator<<(ostream& out,const Cal &x){return out<<x.a;}
经验证,const Cal &x可写成Cal x,但不可以Cal &x
四、赋值运算符=(见深拷贝部分)
五、关系运算符
六、仿函数(),相当于类对象名做函数名,或者直接类名()
class MyAdd{public:int operator()(int num1 ,int num2){return num1 + num2;}};
MyAdd myadd;
调用myadd(100, 100)或MyAdd()(100, 100)

继承方式
基类class Base{public:int x;protected:int y;private:int z;};
派生类公有继承class Son1 :public Base{};//x是public,y是protected,z不可访问
派生类保护继承class Son2 :protected Base{};//x是protected,y是protected,z不可访问
派生类私有继承class Son3 :private Base{};//x是private,y是private,z不可访问
注意:父类中所有非静态成员属性都会被子类继承下去,三种继承的派生类对象的大小都是12B(x,y,z)

继承中的构造与析构顺序
先构造父类再构造子类 ,析构的顺序与构造的顺序相反(记住父类先入栈)

继承中的非静态同名成员(包括属性与方法)
子类对直接访问或调用是用子类的,要访问从父类继承的要加作用域
class Base{
public :
    void func(){}
    void func(int a){}
    int m_A=10;
};
class Son :public Base{
public:
    Son(){}
    void func(){}
    int m_A;
};
对类对象 Son s;
同名成员属性调用 s.m_A与s.Base::m_A
同名成员方法调用 s.func()与s.Base::func();与s.Base::func(100);
注意不能s.func(100);子类中出现的和父类同名成员函数,会隐藏掉父类中所有同名成员函数

继承中的静态同名成员(包括属性与方法)
class Base{
public :
    static int m_A;
    static void func(){}
    static void func(int a){}
};
int Base::m_A = 100;
class Son :public Base{
public:
    static int m_A;
    static void func(){}
};
int Son::m_A = 200;
对Son s;
访问属性可以用对象s.m_A与s.Base::m_A或用类Son::m_A与Son::Base::m_A
访问方法可以用对象s.func();与s.Base::func();或用类Son::func();与Son::Base::func();与Son::Base::func(100);
注意:不能Son::func(100);,理由同非静态

多继承与同名成员
class Base1{public:int m_A;};
class Base2{public:int m_A;};
class Son :public Base1, public Base2{};
由于同名对Son s;访问m_A用s.Base1::m_A与s.Base2::m_A

菱形继承(虚继承)
class Father{public:int m_A;};
class Base1:virtual public Father{};
class Base2:virtual public Father{};
class Son:public Base1, public Base2{};
对Son s;可以访问s.m_A因为只有一个m_A(但sizeof(s)是12B,因为有3个指针)
原理:    每个类一个虚基类表,虚继承时对每个继承的属性记录一个虚基类指针vbptr而非复制实体
    每个虚基类指针指向虚基类表空间位置,该位置记录到实际位置的偏移,进而读取所查数据

多态
子类重写父类的虚函数,父类的指针或者引用指向子类对象执行子类方法,函数地址运行时绑定
父类class Animal{public:virtual void speak(){}};
子类class Cat :public Animal{public:void speak(){}};//virtual void speak(){}亦可
指针形式Animal *animal=new Cat;animal->speak();
引用形式Cat cat;Animal &animal=cat;animal.speak();//多用于函数传参Animal &animal
每个类一个虚函数表,当一个函数被virtual修饰就记录一个虚函数指针(4B),指向虚函数表中函数入口地址
这个类的子类若没有重写,则继承下来的指针指向的表中位置记录父类函数入口,若有重写则指向自己的

纯虚函数
虚函数基础上后面加上=0:class Base{public:virtual void func() = 0;};
一、只要有一个纯虚函数,这个类称为抽象类
二、抽象类无法实例化对象,子类必须要重写父类中的纯虚函数,否则也属于抽象类
    
虚析构和纯虚析构
虚析构就是析构函数前加virtual,如:virtual ~类名(){}
纯虚析构就是虚析构基础上把{}改成=0,然后在外部实现,父类析构在子类析构之后,如:
class Animal{
public:
    Animal(){cout << "Animal构造函数调用" << endl;}
    virtual ~Animal() = 0;
};
Animal::~Animal(){cout << "Animal纯虚析构函数调用" << endl;}
纯虚析构属纯虚函数,则此类也属抽象类,无法实例化对象
虚析构的用途:当子类有堆区属性,且用父类指针指向子类对象时
class Cat :public Animal{
public:    Cat(string name){m_Name=new string(name);}//此属性指向堆区
    ~Cat(){if(m_Name!=NULL){delete m_Name;m_Name=NULL;}}
    string *m_Name;
};
Animal* animal = new Cat("Tom");//程序员手动new也由程序员手动delete
delete animal;//父类指针析构时不会调用子类析构函数,子类如有堆区属性会内存泄漏

文本文件操作(四步走)
写:
#include <fstream>
ofstream ofs;//创建
ofs.open("test.txt", ios::out);//打开
ofs << "data" << endl;//写入
ofs.close();//关闭
读:
ifstream ifs;ifs.open("test.txt", ios::in);
if ( !ifs.is_open())return;
第一种:char buf1[1024]={0};while(ifs>>buf1)cout<<buf1<<endl;
第二种:char buf2[1024]={0};while(ifs.getline(buf2,sizeof(buf2)))cout<<buf2<<endl;
    //C语言下ifs.getline(数组,个数)
第三种:string buf3;while(getline(ifs,buf3))cout<<buf3<<endl;//C++下getline(流,串)
第四种:char c;    while (  (c = ifs.get()) != EOF  )cout << c;}//c语言下ifs.get()获字符
ifs.close();
二进制文件操作
创建输出文件流并打开:ofstream ofs("person.txt", ios::out | ios::binary);
取数据地址并转类型写:ofs.write((const char *)&p, sizeof(Person));
创建输入文件流并打开:ifstream;ifs.open("person.txt", ios::in | ios::binary);
创建变量接收读的数据:ifs.read((char *)&p, sizeof(Person));

流对象
ofstream写操作
ifstream读操作
fstream读写操作
打开方式
ios::in只读
ios::out只写
ios::ate初始位置文件尾
ios::app追加
ios::trunc先删再创建
ios::binary二进制方式(默认是文本ASCII形式的)

函数模板
template<typename T> void mySwap(T &a, T &b){}
传入时T必须全一致,即不能int a;char b;mySwap(a,b);
传入时T必须能推断,否则显式指出类型,即template<class T>void func(){}调用时写func<int>();
隐式类型转换
自动类型推导,不可以发生隐式类型转换
显示指定类型,可以发生隐式类型转换
template<class T>T myAdd0(T a,T b){return a+b;}
int a = 10,b = 20;char c = 'c';
cout<<myAdd0(a,c)<<endl;//不可以
cout<<myAdd0<int>(a,c)<<endl;//可以
函数模板重载
对int a = 10,b = 20;有:
void myPrint(int a,int b){cout<<"普通函数"<<endl;}//myPrint(a,b);最佳匹配
template<class T>void myPrint(T a,T b){cout<<"模板"<<endl;}//myPrint<>(a,b);空模板参数
template<class T>void myPrint(T a,T b,T c){cout<<"重载模板"<<endl;}//myPrint(a,b,100);重载
注意:提供函数模板后最后别提供普通函数(防止二义性)
注意:自动推导不能推出用户设计的类,此时需要具体版本代码
template<class T>bool myCompare(T &a, T &b){}//这是一般函数模板
template<> bool myCompare(Person &p1, Person &p2){}//这是Person版本具体实现

类模板
template<class NameType, class AgeType=int>//就是把T改成多个类型名称
class Person{
public:    Person(NameType name,AgeType age){
        this->m_Name = name;
        this->m_Age = age;
    }
    NameType m_Name;
    AgeType m_Age;
};
调用Person<string> p1("孙悟空", 999);//AgeType有默认类型,故只需string
注意:模板参数列表有默认参数仅限类模板,函数模板不能(函数模板就是template<>后跟函数)
注意:自动类型推导使用方式仅限函数模板,类模板不能(类模板就是template<>后跟类)
注意:类模板中成员函数在调用时才去创建

类模板对象做函数参数传入写法
类:    template<class T1,class T2>class Person{T1 m_Name;T2 m_Age;}
对象:    Person<string, int>p("孙悟空", 100);
一、void func(Person<string, int>&p){}
二、template<class T1,class T2>void func(Person<T1, T2>&p){}
三、template<class T>void func(T &p){}
调用:    func(p);

类模板的继承
对父类template<class T>class Base{T m;};
有子类class Son:public Base<int>{};//显示标明继承的类型
或子类template<class T1,class T2>class Son2 :public Base<T2>//依然是T2来继承

类成员模板成员函数类外实现
类定义:template<class T1,class T2>class Person{public:Person(T1 name, T2 age);}
类外方法:template<class T1,class T2>Person<T1, T2>::Person(T1 name, T2 age){}

类模板分文件编写
类模板中成员函数创建时机是在调用阶段,导致分文件编写时编写时链接不到
解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一文件中,并更改后缀为.hpp

全局函数类内实现(加friend就可以了,如果类内声明类外定义会很麻烦)
template<class T1,class T2>class Person{friend void printPerson(Person<T1,T2> p){}}

C++关键字全解析
alignas        指定某个变量按照其他数据类型对齐
alignof        给出指定数据类型内存对齐的字节数
asm        启动内联汇编函数,必须结合汇编指令一起出现,用大括号或者空括号包围指令
auto        自动识别元素类型
bool        布尔类型
break        跳出循环
case        switch语句分支
catch        捕捉异常
char        字符型数据
char16_t    16位字符型数据
char32_t    32位字符型数据
class        类
const        常数
const_cast    把常量指针或引用转换成普通可变
constexpr    同const可把变量设为常量,但还可用于函数和类构造函数,编译时就计算返回值
continue    下一轮循环
decltype    求表达式的类型,如double a;decltype(a) b;第二句相当于double b;
default        switch语句默认分支
delete        释放堆空间
do        do语句
double        双精度精型
dynamic_cast    把指针或引用的类型进行转换,失败时值为空
else        if语句
enum        枚举元素
explicit    声明类构造函数是显示调用
export        导出模板类对象和模板函数,在其他文件可用
extern        在其他文件下定义的变量
false        假
float        单精度浮点型
for        for语句
friend        友元
goto        跳转
if        if语句
inline        内联函数, 编译器会将相关函数内联展开, 而不是按通常的函数调用机制进行调用
int        整型
long        长型
mutable        常函数中可改的变量
namespace    命名空间
new        申请堆空间
noexcept    承诺某一个函数是不会抛出异常
nullptr        空指针
operator    操作符
private        私有
protected    保护
public        公共
register    寄存器
reinterpret_cast指针与整数之间转换,类似哈希
return        返回
short        短型
signed        有符号
sizeof        求所占空间
static        静态对象
static_assert    静态断言,第一个参数常量表达式的值为真就继续,否则报错,信息是第二个参数信息
static_cast    基类/子类的指针/引用转换,基本数据类型转换,任意类型转成void,
        不安全,转换失败也不返回NULL(区分dynamic_cast)
struct        结构体
switch        switch语句
template    模板声时,template<class T>bool equal(const T& a, const T& b){return a==b;}
this        本对象指针
thread_local    所修饰变量在线程创建时生成,线程结束时销毁,每个线程有自己的变量副本
throw        抛出异常
true        真
try        尝试某种操作
typedef        类型名重定义
typeid        获变量类型,int i = 3;则typeid(i)等于typeid(int)
typename    表示类型,template<typename T>作用同template<class T>,用于区分class表示类

关于const_cast的例子:(用于const与非const之间)
const int a=10;
const int*p=&a;
int*q=const_cast<int*>(p);//不能int*q=p;因为const int*不能转成int*
*q=20;

关于dynamic_cast的例子:(用于继承之间)
class Base{public:virtual ~Base(){}};
class Derived : public Base{};
    Base* p = new Base;
    Derived* pd = dynamic_cast<Derived*>(p);
    //p是NULL,若没有virtual编译不通过

    Derived* p = new Derived;
    Base* pd = dynamic_cast<Base*>(p);
    //p不是NULL,若没有virtual编译也通过

关于export的例子:
// out.h:(声明头文件——只包含out函数的声明信息)
template<class T> void out (const T& t);
// out.cpp:(定义代码文件——包含out函数的声明[通过include]和定义等全部信息)
#include <iostream>
#include “out.h”
export template<class T> void out (const T& t) {std::cerr << t;}
//user.cpp:(用户代码文件——包含函数的声明头文件后就可以使用该函数)
#include“out.h”

关于explicit的例子:
class Circle{  
public:    explicit Circle(double r) : R(r) {}  
    explicit Circle(int x, int y = 0) : X(x), Y(y) {}  
    explicit Circle(const Circle& c) : R(c.R), X(c.X), Y(c.Y) {}  
private:  double R;int X;int Y;  
};
正确构造调用:Circle A(1.23);Circle B(123);Circle C(A);
错误构造调用:Circle A=1.23;Circle B=123;Circle C=A;Circle A=Circle(1.23);Circle B=Circle(123); 
注意一定是用于修改单参构造函数,因为无参与多参没有隐式构造
 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/50786?site
推荐阅读
相关标签
  

闽ICP备14008679号