C++学习笔记(一)

来源:互联网 发布:上海男人 知乎 编辑:程序博客网 时间:2024/05/21 06:35

1、用参数初始化列表初始化成员变量比较好,一个很重要的优点是:可以初始化const成员变量,不能在函数体内初始化const成员变量。

2、带默认参数的构造函数:

 book(char* a, double p = 5.0);  //类内声明时指定默认参数 book::book(char* a, double p)  //在定义函数的时候可以不指定默认参数{    title = a;    price = p;}book Harry("Harry Potter", 49.9);//使用1book Gone("Gone with the Wind");//使用2

需要说明的是带默认参数的构造函数,其默认参数必须置于参数列表的结尾,默认构造函数不带参数时要和其他构造函数分开,容易混淆。

3、类内声明了其他构造函数后,编译器是不会再生成默认构造函数,这是需要手动创建。

4、类中显式地声明了默认构造函数,无论其属性被设置为public、private还是protected,系统都不会再自动生成默认构造函数了

5、转型构造函数,只有一个参数

class Age{public:    Age(int a){age = a;}private :    int age;}

在本例中Age(int a)即为一个转型构造函数,该构造函数仅有一个参数,该构造函数是将int型转换为类对象类型的。

class student{public:    student(){}    student(char * n){name = n;}private :    char * name;}void fun(student s); //函数声明char * name = “Harry Potter”;fun(name);

使用explicit关键字的好处就在于将难以察觉的后期运行时可能会出现的错误提前到了编译期,如此一来改正错误就比较容易了。

explicit student(char * n){name = n;}char * name = “Harry Potter”;fun(name);//编译器无法通过

6、拷贝构造函数
如果类的设计人员不在类中显示的声明一个拷贝构造函数,则系统会自动地为类生成一个拷贝构造函数。自动生成的拷贝构造函数功能简单,只能将源对象的所有成员变量一一复制给当前创建的对象。
只能是对象的引用

book(book &b);book(const book &b);book(const book &b, price = 5.0);

7、析构函数
该函数在main函数退出前被系统自动调用,有多少对象注释,析构函数就调用多少次
先构造的后析构,后构造的先析构,建议为每个带有成员变量的类设计一个默认构造函数

8、在每一个成员函数中都包含一个常量指针,我们称其为this指针,该指针指向调用本函数的对象,其值为该对象的首地址。通过该指针,我们可以在成员函数的函数体内访问对象。其中this是C++的一个关键字
this指针只能用于非静态成员函数内

class book{public:    book(){price = 0.0; title = NULL;}    static void setprice(double price)    {        this->price = price;//compile error    }private:    double price;    char * title;};

9、new和delete操作符
当我们需要为类对象动态分配存储空间时,我们应该使用C++语言提供的new与new[]操作符,而不要使用C语言提供的malloc函数。
虽然malloc函数具有分配存储空间的功能,但是这些函数除了分配存储空间外,不会调用类的构造函数。而C++语言提供的new和new[]操作符则不会如此,使用它们为对象分配存储空间的同时,它们也会调用相应的构造函数。
操作符delete和delete[]在释放对象存储空间的同时也会调用析构函数,而free函数则不会调用析构函数

10、const关键字
const成员变量的初始化只有唯一的一条途径:参数初始化表
常成员函数:不能修改成员变量的值,函数定义和声明的尾部加const

class book{public:    book(){}    book(char* a, double p = 5.0);    void setprice(double a);    void settitle(char* a);    double getprice()const;       char * gettitle()const;private:    double price;    char * title;};double book::getprice()const{    return price;}char * book::gettitle()const{    return title;}

const成员函数是不能调用类中非const成员函数的(语法上防止修改变量的值)
const对象:我们一旦将对象定义为常对象之后,该对象就只能调用类中的常成员函数了。
const 类名 对象名(实参名);
类名 const 对象名(实参名);
常对象修改非常成员变量:
常对象中普通的成员变量是不能被修改的。为了满足这一需求,C++提供了mutable关键字。
mutable int var;
然后,只需要通过常对象调用const成员函数修改该变量即可。

void display(const book &b){        b.setprice(59.9);  //compile error setprice()为非常成员函数,常引用(对象)不能调用非常成员函数    cout<<"The price of "<<b.gettitle()<<" is $"<<b.getprice()<<endl;  //ok}

11、析构函数就是用于回收创建对象时所消耗的各种资源
没有参数,不能被重载,因此一个类也只能含有一个析构函数

12、有元函数
除了顶层函数可以被定义为友元函数之外,其它类的成员函数同样可以声明为本类的友元函数
友元关系是单向的,而不是双向的,友元关系不能传递,友元类会破坏数据的安全性,使用时宁缺毋滥

13、继承
如果在继承过程中不指名继承方式时,编译器系统会默认继承方式为private或protected属性。
1) public继承方式

基类中所有public成员在派生类中为public属性;基类中所有protected成员在派生类中为protected属性;基类中所有private成员在派生类中不可访问。2) protected继承方式基类中的所有public成员在派生类中为protected属性;基类中的所有protected成员在派生类中为protected属性;基类中的所有private成员在派生类中不可访问。3) private继承方式基类中的所有public成员在派生类中均为private属性;基类中的所有protected成员在派生类中均为private属性;基类中的所有private成员在派生类中均不可访问。

private私有成员无法被继承。
可以用using改变派生类对基类的访问权限。

class codingbook: public book{public :    void setlang(language lang);    language getlang(){return lang;}private:    language lang;    using book::setprice;// void setprice(double a);为基类的public成员};

14、c++名字隐藏

class basic{public:    void setx(int a){x = a;}    void sety(int b){y = b;}    int getx(){return x;}    int gety(){return y;}private:    int x;    int y;};class derived : public basic{public:    void setx(char *a){x = a;}    char* getx(){return x;}private:    char * x;};int main(){    derived d1;    d1.setx("class");   //OK    d1.setx(50);        //compile error    d1.basic::setx(50); //OK    return 0;}

从上例中,我们可以看出被遮蔽了的基类的成员变量或成员函数并非是没有继承过来,而仅仅是被派生类的同名成员变量和成员函数给遮蔽了,调用的时候需要用到类名加上域解析操作符。
假设类C继承自类B,类B继承自类A。那么类C中的除了能够继承B类的成员函数和成员变量外,同样也能继承B类继承自A类的所有成员。换言之,类C可以继承来自类A的所有成员。因此继承既可以是直接继承,也可以是间接继承。

15、派生类构造函数的调用规则
派生类构造函数可以自动调用基类的默认构造函数而无需显式调用

16、虚基类

class A{public:    void setx(int a){x = a;}    int getx(){return x;}private:    int x;};class B: virtual public A{public:    void sety(int a){y = a;}    int gety(){return y;}private:    int y;   };class C: virtual public A{public:    void setz(int a){z = a;}    int getz(){return z;}private:    int z;};class D: public B, public C{    //......};int main(){    D test;    test.setx(10);    cout<<test.getx()<<endl;    return 0;}

类B和类C都是继承类A都是虚继承,如此操作之后,类D只会得到一份来自类A的数据。在本例的主函数中,定义了类D的对象test,然后通过该对象调用从类A间接继承来的setx和getx成员函数,因为B和C继承自类A采用的是虚继承,故通过D调用setx和getx不会有命名冲突问题,因为D类只得到了一份A的数据。

17、多态 virtual
要想形成多态必须具备以下三个条件:
必须存在继承关系;
继承关系中必须有同名的虚函数;
存在基类类型的指针或引用,通过该指针或引用调用虚函数。
和普通的成员函数一样,虚成员函数同样可以被继承。
使用多态会降低程序运行效率,使用多态的程序会使用更多的存储空间,存储虚函数表等内容

18、虚析构函数
构造函数是不能声明为虚函数的
通常来说,如果基类中存在一个指向动态分配内存的成员变量,并且基类的析构函数中定义了释放该动态分配内存的代码,则应该将基类的析构函数声明为虚函数。
只有非静态成员函数才可以成为虚函数,而静态成员函数不能声明为虚函数。

class test{public :    virtual test(){a = new int[5];} //error    static void g();          //ok    virtual void f();         //ok    virtual static void h();  //compile error    virtual ~test(){delete[] a;} //okprivate:    int * a;};

19、函数签名

int f(int, double)void f(int, double)

两个函数的函数名相同,函数参数个数相同,函数参数类型相同,函数参数顺序相同,如此一来两个函数的函数签名是相同的。但是这两个函数的返回值不同,仅凭函数返回值,编译器无法区分这两个函数,编译器提示语法错误。
函数重载是编译期绑定,它并不是多态
覆盖首先要求有继承关系,其次是要求构成继承关系的两个类中必须具有相同函数签名的成员函数,并且这两个成员函数必须是虚成员函数,具备这两个条件后,派生类中的虚成员函数则会覆盖基类中的同名的虚成员函数。如果我们通过基类指针或引用来调用虚成员函数,则会形成多态。

20、纯虚成员函数

class base   //base类为抽象基类{public :    virtual void display() = 0;    //......};int main(){    base b; //compile error    return 0;}

抽象基类是无法用于创建对象的,纯虚成员函数可以被派生类继承,如果派生类不重新定义抽象基类中的所有(有多个则要重新定义多个)纯虚成员函数,则派生类同样会成为抽象基类,因而也不能用于创建对象。
只有类中的虚函数才能被声明为纯虚成员函数,普通成员函数和顶层函数均不能声明为纯虚成员函数。

21、string类型
与C风格的char型数组不同,string类型的变量结尾是没有 ’\0’ 的,string类型的本质是一个string类

22、void *calloc(size_t n, size_t size);
功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。