C++基础

9 篇文章 0 订阅

①引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
②声明的时候必须初始化,一经声明,不可变更。
③可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
④&符号前有数据类型时,是引用。其它皆为取地址。


引用的本质:

void motifyA(int *const a)   
{
    *a = 100;
}

void motifyB(int &a)
{
    a = 1000; 
}

①引用等价于常量指针
②在给引用初始化的时候,编译器在默认的情况下对右值取地址:&
③在给引用赋值的时候,编译器在默认情况下对左值取内容:*
④在理解的时候:引用即为常量指针;在使用的时候:引用是变量的一个别名
⑤引用和指针占用的内存大小一样;使用时可能以为引用不占内存,其实是C++做的一个隐藏
⑥若是对初始化过的引用求大小的时候,求的是引用对应指向的空间大小,而不是引用的大小
⑦引用和指针都有三步:定义两个变量、建立两个变量的关系、使用指针\引用变量


int value_a = 0;
int getA1()
{
    int a = 10;
    return a;
}
value_a = getA1();   //正确,局部变量的值拷贝

int &getA2()
{
    int a = 10;
    return a;
}
value_a = getA2();//错误,局部变量引用的值拷贝,局部变量会自动释放
int &r_a = getA2(); //错误,不能将局部变量的引用用于引用的初始化

int &getBB()
{
    static int b = 100;
    return b;
}
int &r_b = getBB(); //正确,静态变量、指向堆开辟空间的变量可以用于引用初始化

三目运算符之所以可以做左值:三目运算符返回的是一个变量的引用,而不是变量的值。

getBB()=1000;//正确,函数返回的变量是引用

指针变量也是变量,指针作为函数指针的时候也可以传递指针的引用,效果与二级指针等价。

int getTeacher02(Teacher* &tr) //tr 就是main中的tp 
{
    ......

    return 0;
}
void freeTeacher02(Teacher * &tr)
{
    ......
}
Teacher *tp = NULL;
getTeacher02(tp);
freeTeacher02(tp);

int a=0;
const int b=0;
const int &aa1=a;//正确
const int &bb1=b;//正确
int &aa2=a;//正确
int &bb2=b;//错误
//引用不能把原变量的操作属性扩大

const int &re2 = 10;//正确
int &re2 = 10;//错误
//10本没有内存空间,但是上面的情况出现时,为10创建一个临时的const匿名空间

命名空间的作业:防止不同命名空间相同名字的发生冲突


①内联函数没有压栈、出栈,直接整合到调用的地方。函数声明、定义前使用inline,否则变为普通函数。
②内联函数不能太复杂(不能存在循环语句、条件判断语句、函数体不能太大),否则变为普通函数。
③不能对内联函数取地址[内联函数没有地址],否则变为普通函数
④宏函数是预处理器展开、没有词法校验;内联函数只是没有压栈、出栈,其他和普通函数一样,其是编译器处理的。
⑤内联函数过于复杂,以至于开销远大于出栈、进栈时,内联函数将毫无意义。


①C++函数的参数可以设置默认值,当有实参传入,则以实参为准。
②函数只设定一部分有默认参数,则这些具有默认参数的部分要放在函数参数列表的右边。
③占位符只是为扩展C++做准备,若是不想传这个没有意义的参数,我们给占位符设默认值

int fun(int n,int); fun(1,2);
int fun(int n,int = 10);fun(1);

函数重载:
①函数名相同;参数个数不同,参数的类型不同,参数顺序不同,均可构成重载;返回值类型不同则不可以构成重载。
②函数重在后最好不要有默认值,会出现二义性
③重载函数的调用首先会严格匹配参数,若是没有匹配的,则会执行隐式转换寻求匹配的。
④匹配过程;相同函数名、返回值->精确匹配->默认参数匹配->转换匹配->多备选则报二义性
⑤一种函数指针只能指向其对应的一种重载函数的类型,不可能通过函数指针发生函数重载。


类的封装:将数据和方法进行封装,对数据和方法进行访问控制。
struct和class对数据进行封装的区别:
使用struct定义类时。所有成员的默认属性为public
使用class定义类时。所有成员的默认属性为class


构造函数:

class 类名
{   
    类名(形式参数)    
    {   
        构造体
    }   
}   

①在对象创建时自动调用,完成初始化相关工作。 ?
②无返回值,与类名同,默认无参,可以重载,可默认参数。
③一经实现,默认不复存在。
析构函数:

class 类名
{   
    ~类名()  
    {   
        构体
    }
}

①对象销毁时,自动调用。完成销毁的善后工作。 ?
②无返值 ,与类名同。无参。不可以重载与默认参数
注意:
①析构函数的作用,并不是删除对象,而在对象销毁前完成的一些清理工作。
②析构函数的调用顺序和构造函数调用的顺序相反


构造函数的分类:
①无参构造函数
②有参构造函数
③拷贝构造函数:由已经存在的对象构造一个新的对象。

class 类名
{   
    类名(const 类名 & 3another)     
    {   
        拷贝构造体
    }       
}

类名 实例名(实例对象);//调用拷贝构造函数
类名 实例名=实例对象;//调用拷贝构造函数
类名 实例名;//没有调用拷贝构造函数,调用了无参构造函数
实例名=实例对象;//调用了等号赋值操作

类名 fun(){}

①函数参数为一个临时对象的时候,传递实参给形参会调用形参对象的拷贝构造函数
②没有变量接受函数返回对象[非指针、引用]:函数返回值为一个对象时,会参加一个临时对象进行赋值操作,调用临时变量的拷贝构造函数给临时变量赋值,之后立即调用临时对象的析构函数,释放临时变量的资源。
③有变量接受函数返回对象[非指针、引用]:
1)[类名 对象名=fun();]被接收的临时对象转正,不立即销毁,将临时变量的名字定为接收变量的名字,即会给转正后的临时变量取名,生命得到延伸。
2)[类名 对象名;对象名 = fun();]调用接收变量=操作符,此时临时对象没有自己的别名,编译器立即销毁临时对象。

注意:
①若是显示的提供构造函数的时候,默认的无参构造函数将会隐藏。
②显示的提供一个拷贝构造函数的时候,默认的拷贝构造函数会被隐藏。
③显示的析构函数被提供的话,默认的析构函数将被隐藏


①类内部的对象初始化的顺序和对象定义的顺序有关,与初始化列表中的顺序无关;构造函数与析构函数的调用顺序相反。
②常量只能通过初始化列表初始化,不能通过赋值的操作进行初始化。
③[ABCD(400, 500, 600);]匿名对象一旦创建,之后立即释放,由于匿名函数没有名字。
④[ABCD abcd = ABCD(100,200,300);]只调用匿名对象的构造函数一次就好,并给匿名对象取名,这个类似于函数的返回值为匿名对象的情况。
⑤构造函数无法嵌套构造函数给当前对象的变量赋值,因为此时是创建了另一个临时对象


new和delete:
malloc、free是函数,new、delete是操作符,new、delete效率想对较高,最好各自成对使用

new能够完成malloc的功能,并且自动调用构造函数
delete能够完成free的功能,并自动调用析构函数。[delete[] p1;delete p2]

对于基本类型:delete可以释放malloc开辟的内存,free能够释放new开辟的内存
对于自构造类:使用new、delete,因为new、delete会自动调用构造函数和析构函数


static变量和函数:
1)C语言中:
static在C语言中主要是两种用法
1、修饰局部变量:static局部变量初次定义时就要初始化,且只能初始化一次。
2、修饰函数和全局变量:制该全局变量或者函数的作用域仅限于本文件。
扩展:register为寄存器变量,存放在寄存器里面,调用速度快。在C语言中register变量不能取地址,会报错。而在c++中,对register做了增强,当C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。
2)C++语言中:

class AA
{
public:
    static int a;
    static void setB(){b=0;};
    static int getB(){return b}
privatestatic int b;
}
int AA::a=0;//在类的外面初始化静态成员变量。静态成员变量不占用对象的空间,是在全局区开辟内存空间。
int AA::b=0;//静态成员变量只能在[类外初始化]

①同一个类的不同实例对象共享静态成员变量
②可以直接通过类名[AA::a;]来修改静态成员变量,也可以通过成员函数来读写静态成员变量
③使用静态成员函数[[AA::getB();]]调用private static 成员变量。静态成员函数只能调用静态成员变量。非静态成员函数可以调用任意类型的成员变量。函数、静态成员变量不占用对象的空间。

static成员函数没有this指针,故不能操作非静态成员变量。this指针本质是一个对象[常指针]
int getX()const{};this本身是[int * const this],加了一个const->[const int * const this]


全局函数:类外面的函数
成员函数:类里面的函数
同类之间无私处,private成员变量可以直接调用,传入的对象也可以。
数组的内存空间的释放需要使用:delete[] 指针/数组名


friend函数破坏了类的封装性,要使用friend函数就要在类中声明friend函数[位置任意]
例子:friend 返回值 函数名(参数列表); 只要将这个在类中声明一下,其他的和全局函数无差,此后friend函数可以直接在函数中访问对象的私有成员。

声明其他类(B)的成员函数为自己(A)的友元函数:
在A类中任意位置:friend 返回值 B类名::函数名(参数列表);
例子:

class A;//类A的声明
class B//类B的声明
{
    返回值 函数名(A &a);
}
class A
{
    friend 返回值 B类名::函数名(A &a);//声明友元函数
}
返回值 B类名::函数名(A &a){}//实现友元函数,友元函数必须在[具体类A]后实现

目的:为的就是在B中操作A的私有成员变量

class A
{
    friend class B;//声明B类是我的友元类,可以在任意位置声明
}
class B
{

}

类B的所有成员函数都是类A的友元函数,能存取类 A的私有成员和保护成员

同类之间无私处,异类之间有友元。

总结:
友元不具有继承性得
友元的关系是单向的
友元不具有传递性的


1)操作符重载:给操作符赋予新的含义
普通双目运算符格式:返回类型[引用、指针、临时变量] operator运算符(参数列表){}
实现方式有两种:这两种只能存其一,否则会有二义性
①友元函数
②成员函数
调用方式:
①operator运算符(对应的实参):operator+(实参1,实参2)[友元方式下]
②operator运算符(对应的实参):实参1.operator+(实参2)[成员函数下]
③实参1 运算符 实参2:实参1 + 实参2[两种情况都可以用,两者都存在时有歧义]
2)重载规则:
①只允许重载已有的操作符
②不允许重载的操作符:.、.[(a.p)==a.*p]、::、?:
③重载不能改变原有操作符的个数
④不能改变重载运算符的优先级
⑤重载的运算符不能改变运算符的结合性,例如=是从左往右结合的
⑥重载的运算符不能有默认参数
⑦重载的运算符至少含有一个重新定义该运算符的对象
⑧运用于对象的操作符除了=、&两个操作符不用重载,别的操作符都要重载
⑨重载操作符实现后,内部意义不变
⑩重载的实现可以说成员函数、友元函数、普通函数

++a效率比a++效率高:++a是一条支流,a++是两条指令。

Complex & operator++()//前++的成员函数,可以连续前++,返回引用
{
    ...
    return *this;
}
const Complex operator++(int)//后++的成员函数,不可以连续后++,返回常量
{
    ...
    return *this;
}

//只能使用友元方式,不能使用成员函数的方式重载<<、>>运算符达到预期效果
ostream & operator<<(ostream &os, const Complex &c)
{
    os << "( " << c.a << "+" << c.b << ")" << endl;
    return os;
}
istream & operator>>(istream &is, Complex &c)
{
    cout << "a : ";
    is >> c.a;
    cout << "b :";
    is >> c.b;

    return is;
}

注意:只要对象中有申请堆内存空间的操作,我们就要重写拷贝构造函数、=操作符。
重写拷贝构造函数:防止浅拷贝、内存重复释放[源内存]、这里不考虑内存泄漏的问题
重写=操作符:防止内存泄漏[原内存]、内存重复释放[源内存]、浅拷贝[指针指向拷贝]
[重写=操作符]比[重写拷贝构造函数]多了清理原内存空间的操作。


重载的new和delete虽然使用malloc、free实现的,但是还是会调用构造函数和析构函数;重载的目的是为了增加自己的业务。
注意:new和delete重载适用于极个别情况需要定制的时候才用的到。一定很少用。

void * operator new (size_t size)       
{   
    cout<<"new"<<size<<endl;    
    return malloc(size);    
}   
void operator delete(void *p)   
{   
    cout<<"delete"<<endl;   
    free(p);        
}   
void * operator new[] (size_t size) 
{   
    cout<<"new[]"<<size<<endl;  
    return malloc(size);    
}   
void operator delete[](void *p)     
{   
    cout<<"delete[]"<<endl; 
    free(p);    
}
//重载的new、delete会潜在的调用构造函数、析构函数
A * cp = new A; //个人认为参数[size_t size]会自动计算对象的大小
delete cp;  
A * cpa = new A[20];//个人认为参数[size_t size]会自动计算对象的大小乘以20的值   
delete []cpa;

注意:new、delete最好局部重载,即类内部重载

仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

class 类名    
{   
    返值类型 operator()(参数类型)函数体
}

注意:仿函数一帮和容器配合使用

class Print
{
public:
    void operator()(int value)
    {
        cout << value << endl;
    }
};
vector<int> v1; //数组 添加10个元素
for (int i = 0; i < 10; i++)
{
    v1.push_back(i + 10);
}
Print pObj;
for_each(v1.begin(), v1.end(), pObj); //pObjd(value)

&&和||操作符不要重载,如果重载会失去短路功能。
A&&B:A假时就不会检测B
A||B:A真时就不会检测B
但是重载这两个操作符后,这个功能就会丧失。


智能指针:就是将一个new出来得对象B放入另一个对象A中,在对象A销毁之前回调用对象A的析构函数,析构函数中会释放掉B对象所占据的内存空间,到达指针智能化。


string类:可以直接赋值字符串,可以进行字符串的各种操作[自动分配、释放内存空间,可以进行=、+等操作],执行=操作、拷贝构造函数等6的时候不用担心浅拷贝。

当使用字符串常量初始化对象的时候,我们要另开空间,不要直接用指针指向常量字符串,还有一个重要的点时,每次我们都要多申请一个空间用来存放’\0’。


has:类A是类B的成员变量
use:类A是B的成员函数的形参
is:类A是类B的父类,即类B继承类A

当子类继承父类的private成员变量的时候,我们通过以下方式初始化继承的private变量

class A
{
public:
    A(int a,int b)
    {
        ...
    }
    void printS()
    {
        ...
    }
public:
    int c;
private:
    int a;
    int b;
} 

class B:public A
{
public:
    //父类的private成员变量用父类的构造函数初始化
    B(int a,int b,int c):A(a,b)
    {
        ...
    }
    void print()
    {
        //调用父类的成员函数
        A::printS();
        //调用子父类重名成员变量中父类变量
        cout<<A::c<<endl;
        ...
    }
private:
    int c;
}

继承是代码的复用,而不是内存共用;子类默认只是调用父类的构造函数、析构函数操作继承的成员变量,除非调用的构造函数是无参的,否则需要显示的调用父类相应的构造函数。


继承类的兼容性原则:
①子类可以给父类初始化
②父类指针可以指向子类,反之不可以:这样才可以保证成员变量、方法等正常的调用。
③父类引用可以指向子类对象,反之不可以
总结:[赋值兼容]达成了类的多态的功能。父类—>子类


静态成员变量:
静态成员变量和普通变量一样的方式被继承,整个类家族共享一个static成员变量。

虚继承:当B、C类继承同一个类的时候,我们使用virtual关键字修饰继承关系

class A
{
public:
}
class B:[virtual] public A//虚继承
{
public:
}
class C:[virtual] public A//虚继承
{
public:
}
class D:public B,public C
{
public:
}

class A
{
public:
    [virtual] void print(){}//将成员函数定义为虚函数,是多态的前提
}
class B:public A
{
public:
    //父类不加virtual是重定义,父类不加virtual是重写。重写!=重载
    [virtual] void print(){}//virtual可加,可不加;
}
void fun(A *p)
{
    p->print();
}

A a;
B b;
fun(&a);fun(&b)//父类不加virtual,两个打印父类的;父类加virtual,两个打印各自的

重点:多态的意义就是同一操作产生不同的现象;架构师运用多态可以达到操作未来的能力。
注意:多态是设计模式的基础,多态是框架的基础。多态是动态联编,重载是静态联编
[动态联编]在[执行]的时候决定执行什么操作,[静态联编]在[编译]的时候决定执行什么操作

虚析构函数:
子类对象做函数的参数[使用父类指针指向子类],若析构函数不是虚函数,在析构的时候只调用父类的析构函数,不会调用子类的析构函数,反之会调用与构造反序的析构函数,所有内存空间释放。

class B
{
}
class C:public B
{
}

void func(B *p){delete p;}

C c;
func(&c);//报错,对象在栈中开辟内存:手动一次,main中自动一次

C *cc = new C;//对象在堆中开辟内存,指针在栈空间,对象在堆空间
func(cc);

虚函数表:Parent *p = new Chiled;//p指针指向child对象的内存空间[该空间前有虚函数指针]
每个类生成对应的对象A时都会生成虚函数指针VPTR指向虚函数表B,虚函数表B存储该对象A虚函数对应的虚函数指针,若是子类没有重写父类的虚函数,虚函数表中的表项[虚函数指针]指向父类实现的虚函数,若是子类重写了该虚函数,则该虚函数表中的表项[虚函数指针]指向子类实现的虚函数。通过上述机制达到多态的效果,多态是动态联编的一种。虚函数比普通函数的执行效率低,因为虚函数的执行前有寻址的操作。

class A
{
public:
    A(int a){ this->a = a; print();}
    virtual void print(){cout<<"A"<<endl;}
private:
    int a;
};
class B:public A
{
public:
    B(int a,int b):A(a){ this->b = b; print();}
    virtual void print(){cout<<"B"<<endl;}
private:
    int b;
};
class C:public B
{
public:
    C(int a,int b,int c):B(a,b){ this->c = c; print();}
    virtual void print(){cout<<"C"<<endl;}
private:
    int c;
};
C c(1,2,3);

构造子类对象之前,先从最高的父类对象开始构造,所以说子类对象的虚函数指针VPTR一次从父类指到子类,每次调用virtual函数的时候,都是调用虚函数指针VPTR当前指向的虚函数表对应的虚函数,每当构造好一个父类后,虚函数指针VPTR立即指向子类的虚函数表。以上程序依次打印A,B,C。

注意:不要在构造函数中调用成员函数,以防使用未初始化的成员变量。
陷阱:使用父类指针指向子类数组的时候,一定要注意到父类和子类的指针步长不一样。
多态总结:同一操作,不同表现形态;多态是基于虚函数的重写实现;多态是动态联编的一种;多态是设计模式的基石,可以调用未来;


纯虚函数:

virtual 类型 函数名(参数表)=0;

当同一个接口虚函数出现在多个继承的类中,我们只要实现一次即可。

只要类中有虚函数,类中析构函数就要为虚析构函数。

只要类的成员变量有指针,我们就要自定义析构函数,防止内存泄漏。

依赖倒转原则:应用层只关心逻辑不关心具体的实现,实现根据具体的具体情况来。


类的实现中,构造函数应只实现最基本的初始化,不应该将业务写在构造函数中;业务应该由对象提供一个初始化的函数来实现对象的各种初始化。

析构函数声明时候为虚函数?
析构函数的作用与构造函数正好相反,是在对象的生命期结束时,释放系统为对象所分配的空间,即要撤消一个对象。
用对象指针来调用一个函数,有以下两种情况:
①如果是虚函数,会调用派生类中的版本。(在有派生类的情况下)
②如果是非虚函数,会调用指针所指类型的实现版本。
★析构函数也会遵循以上两种情况,因为析构函数也是函数嘛,不要把它看得太特殊。 当对象出了作用域或是我们删除对象指针,析构函数就会被调用。当派生类对象出了作用域,派生类的析构函数会先调用,然后再调用它父类的析构函数, 这样能保证分配给对象的内存得到正确释放。
★但是,如果我们删除一个指向派生类对象的基类指针,而基类析构函数又是非虚的话, 那么就会先调用基类的析构函数(上面第2种情况),派生类的析构函数得不到调用。


C语言中:回调函数可以隔离调用者和实现者,可以是一对一、一对多、多对多。
多态是好刀,设计模式是磨刀石。


模板:模板就是对数据类型的参数化

template<class T>
void mySwap(T &a, T &b);//先说明后实现模板函数

template<typename T>//class和typename等效
void print(T &a, T &b)//声明和实现同时实现的模板函数
{
    cout << a << ", " << b << endl;
}
int main(void)
{
    int a = 10;
    int b = 20;
    print<int>(a, b);//打印内容
    mySwap<int>(a, b);//交换内容
}
template <typename T>
void mySwap( T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

函数重载:
①使用模板函数时可以显示的指定类型,也可以不显示的指定类型;显示使用模板时,只能使用模板函数
②当普通函数不需要隐式转换时,优先调用普通函数。
③当普通函数需要隐式转换时,优先调用模板函数。
④模板函数是严格匹配,不会进行隐式转换,即模板函数无匹配时,查看普通函数是否可以隐式转换匹配

编译器对模板机制剖析:
g++ -E hello.c -o hello.i(预处理)//预处理展开各种宏
g++ -S hello.i -o hello.s(编译)//编译生成汇编代码,破解程序一定要懂汇编
g++ -c hello.s -o hello.o(汇编)//生成二进制文件
g++ hello.o -o hello(链接)//生成可执行文件
以上四个步骤,可合成一个步骤
g++ hello.c -o hello(直接编译链接成可执行目标文件)

对模板编译,根据模板生成特定类型[有多少类型就生成多少个n]进行编译[总共n+1次编译]。


//[滥用友元]必须加载以下四行
template<class T>
class Complex;//声明类
template <class T>
Complex<T> mySub (Complex<T> &c1, Complex<T> &c2);//声明友元函数

template<class T> //template <typename T>
class Complex
{
    //如果写一个模板类提供一个<<或>>友元函数,在操作符和参数列表之间要加<T>
    //以上是C++的一个坑。我们最好把友元的实现写在类里面,避免这个坑
    friend ostream & operator<< <T> (ostream &os, Complex<T> &c); //中间多了个<T>
    /*
    //有的IDE不支持上面的写法,我们使用下面的写法
    friend ostream & operator<<(ostream &os, Complex<T> &c);
    {
        os << "( " << c.a << "+" << c.b << "i )" << endl;
        return os;
    }
    */
    //在模板类中[滥用友元]函数,这样会很麻烦,能不用友元函数就不用.
    friend Complex<T> mySub<T>(Complex<T> &c1, Complex<T> &c2);//中间多了个<T>
    /*
    Complex<T> mySub(Complex<T> &c1, Complex<T> &c2)//也可以将这个声明与定义分开
        {
            Complex<T> temp(c1.a - c2.a, c1.b - c2.b);
            return temp;
        }
    */
public:
    Complex()
    {
        this->a = 0;
        this->b = 0;
    }
    //一般的操作符重载添加相关模板样式即可
    Complex operator+(Complex &another);
private:
    T a;
    T b;
};

template <class T>
ostream & operator<<(ostream &os, Complex<T> &c)
{
    os << "( " << c.a << "+" << c.b << "i )" << endl;
    return os;
}

template <class T>
Complex<T>  Complex<T>::operator + (Complex<T> &another)
{
    Complex temp(this->a + another.a, this->b + another.b);
    return temp;
}

template<class T>
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2)
{
    Complex<T> temp(c1.a - c2.a, c1.b - c2.b);
    return temp;
}

注意:模板类最好不要拆开写,因为模板类时二次编译:第一次是模板本身[生成在相应的.h中],第二次是特定的生成类[生成在相应的.cpp中]。在main函数文件中要包含.h、.c两个文件。所以模板类不要使用多文件编写,一定要多文件的话,将.cpp改为.hpp后缀(不建议这样做)。(.hpp既可以当.cpp,也可以当.h)

模板类根据调用代码生成了两个不同类型的类A、B,A、B各自拥有自己的静态成员函数。
使用容器操作对象时,要重写对象的拷贝构造函数、=号操作符,因为容器中会执行赋值操作[深拷贝]。


类型转换:类型 接收 = static_cast<类型>(转换);//增加可读性
父类转化为子类:子类变量 = dynamic_cast<目标类>(父类变量)//本质[原]可行的情况下才可以
去掉const属性:类型 接收 = const_cast<类型>(转换)//本质[原]可行的情况下才可以
重新解释类型转换:reinterpret_cast<目标类型> (标识符)//数据的二进制形式重新解释,不改变其值

注意:一定要知道转换后的结果,一般不建议使用类型转换。


C语言根据返回值查看异常。
C++中的异常:try{}catch(){}、throw
①异常是严格匹配类型的,[…]表示任何异常
②throw有return的结束函数执行的功能
③异常可以一直往上抛,throw可以有具体参数、也可以没有,没有就表示抛出接收到的默认异常。
④栈解旋throw抛出之前,栈中开辟的对象都会被自动的释放

返回类型 函数名(参数列表)throw(可以抛出异常类型的列表){}//只能抛出列表中的异常
返回类型 函数名(参数列表)throw(){}//此函数不能抛出任何的异常
返回类型 函数名(参数列表){}//可以抛出任何异常

异常变量的生命周期:
抛异常的时候回创建一个匿名对象A,匿名对象A会用于创建捕获异常的对象e,此时调用的是该捕获对象e的拷贝构造函数,之后立即将匿名对象A析构函数,匿名对象A析构函数在捕获之前调用。也就是说匿名对象时临时对象


[cin>>]以空格、回车为结束符。

cin.get()每次读取一个字符,没有就阻塞。
ch=cin.get();
cin.get(ch);
cin.get(buf,10,’ ‘);//遇到空格就为止

cin.getline(buf,128);//最多读128个字节

cin.ignore( a, ch );//它表示从输入流 cin 中提取字符,提取的字符被忽略,不被使用。而每抛弃一个字符,它都要进行计数和比较字符:如果计数值达到 a 或者被抛弃的字符是 ch ,则cin.ignore() 函数执行终止;否则,它继续等待。
它的一个常用功能就是用来清除以回车结束的输入缓冲区的内容,消除上一次输入对下一次输入的影响。例如可以这么用,cin.ignore(1024, ‘\n’),通常把第一个参数设置得足够大,这样实际上是为了只有第二个参数 ‘\n’ 起作用,所以这一句就是把回车(包括回车)之前的所以字符从输入缓冲流中清除出去。

cin.putback();//将刚刚读出的字符立马放回缓冲区原来的位置

cout格式化输出意义不大,要的时候再看。


文件的IO操作:就是形式和C语言不一样,其实差不多,用的时候在再看。
ofstream、ifstream文件的输入输出 ctrl+z == EOF3

对象、数字等使用二进制方式操作文件。
ofstream.write()以二进制方式写数据
fout.write((char*)&对象, sizeof(Teacher));
ofstream.read()以二进制方式读 数据
fin.read((char*)&对象, sizeof(Teacher));

总结:
①艺术美方式写数据到文件,就以什么方式从文件读数据,一定要配套。
②到底是使用二进制方式存数据还是文本方式数据,其实差别不大主要注意以下几点:
★换行符:
文本方式下输入输出:’\n’->’\r”\n’;’\r”\n’->’\n’。
二进制方式下输入输出:’\n’->’\n’;’\n’->’\n’。
★存取方式:
文本输出输入:以ASCII方式存取数据
二进制输入输出:以二进制方式存取数据
★推荐方式:
①在非windows平台下,全部数据存取使用二进制方式没问题
②日志等需要查看的文本数据使用文本方式
③数字等保持精度的数据使用二进制,对象等自定义结构使用二进制


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值