C++进阶笔记

思想原则:

以类为例,类最终要处理的是数据,方法只是过程,最终要改变的是private中的数据成员状态。程序设计也是如此,要的是数据。

一、const的作用

const定义变量:定义了一个不可修改的常量;
const用于返回值:通常是用于限定指针和引用,用于防止返回值处于左值时被修改;
const用于参数:防止原数据被修改;
const用于函数名后:防止类成员函数调用时被修改;

二、引用、指针、值传递

引用除了在函数头的参数中添加了&符号,其他行为和值传递是一样的;
引用是一种别名的行为,使用的是原始数据,因此不需要像值传递一样使用匿名变量的额外开销;
指针相对引用比较麻烦,指针需要开辟存储空间用于存储变量的地址,而且使用时需要解除引用,相对引用来说比较麻烦,因此在值传递的过程中,使用引用传递相对来说轻松些。

何时使用值传递

·当数据对象较小,如内置基本类型和小型结构体,且不需要修改原数据时

何时使用指针

·当数据对象是数组,则使用指针,这是唯一的选择
·当数据是基本类型且需要修改原数据时

何时使用引用

·当数据对象是类对象时

何时使用指针或引用

·当数据对象是较大结构体时

三、递归函数

执行递归函数时,递归语句前的程序按照条件正序执行一遍,然后递归语句后的程序倒叙执行一遍。

四、作用域和链接性

首先对变量分类:自动变量(局部变量)、静态变量(区别于new创建的动态变量)、动态变量(new创建的在运行时赋值的变量);
通常,编译器使用三块独立的内存:一块用于自动存储,一块用于静态存储(可能再细分),另一块用于动态存储。

变量类型作用域链接性如何声明
自动变量代码块在代码块中
静态,外部链接性变量全局文件之间不在任何函数内
静态,内部链接性变量此文件内此文件内不在任何函数内,使用static
静态,无链接性变量代码块在代码块内,使用static
const,内部链接性变量此文件内此文件内不在任何函数内,使用const
const,外部链接性变量被引用文件内被引用文件内不在任何函数内,使用extern const
const,无链接性变量代码块在代码块内,使用const

注意:静态成员(static)只初始化一次,

另外,通常函数的链接性为外部性,但是可以使用static使其作用域变成此文件内。

五、构造函数

使用构造函数

常规赋值方法,例如整型和结构体:

//整型
int a=3;
//结构体
struct str={1,"code",1.23};

但这种方式不能给类的对象赋值,因此采用构造函数进行赋值;
构造函数用于给对象的数据成员进行赋值,使用方法如下:

//显示调用
Stock food=Stock(1,"code",1.23);
//隐式调用
Stock food(1,"code",1.23);

默认构造函数

以下用法将调用默认构造函数:

Stock food;

就是只创建对象,但不初始化,则调用默认构造函数进行初始化。
有两种默认构造函数,
一种是给参数提供默认值:

Stock(int a=1,std::string str="code",double c=1.23)

另一种是通过函数重载:

Stock::Stock()
{
    m_a=1;
    m_str="code";
    c=1.23;
}

六、析构函数

和构造函数相对应,构造函数在创建对象时主动调用构造函数进行给数据成员赋值,默认构造函数则会在特定情况下自动调用,析构函数则是在函数过期时自动调用的函数,本身的功能就是自动调用,并不附加额外功能,有些书本的定义实在令人产生歧义!

Stock::~Stock
{
    //detele xxx;
    std::cout<<"此函数终结!"<<std::endl;
}

七、this指针

有时对象调用方法会修改对象的数据成员,这时需要返回新修改后的对象,就可以使用this指针进行返回;
每个对象都有一个this指针指向自己的地址。

八、对象数组

初始化对象数组使用列表初始化方式:

Stock food[3]={
    Stock(1,"one",1.23),
    Stock(2,"two",2.23),
    Stock(3,"three,3.23")
} ;

八、作用域为类的常量

由于类只有在创建对象时才会分配空间,所以在类中无法使用 const int a=3;这种形式,因为此时还未分配空间,有两种方法解决这个问题,
第一种方法是使用枚举,枚举不创建值,只是一种替换,例如下边是使用12替换了Month,有点像宏定义?
第二种方法是使用static,使用static会创建一个常量,此时该常量是存储在静态存储区,而不是在类对象中。

class stock
{
    private:
//第一种是在类中声明一个枚举
        enum{Month=12};
//第二种是使用static
        static const int Day=31;
}

九、友元函数

友元函数是非成员函数,不能被对象调用,但是友元函数的访问权限和成员函数一样,可以访问私有数据成员。
一般将运算符重载函数声明为友元函数,因为这样就可以显示调用参数。

十、隐式转换

1.只有接受一个参数的构造函数才能作为转换函数,使用explicit可关闭隐式转换。

十一、复制构造函数

复制构造函数用于将一个对象复制到新创建的对象中,即新建一个对象并将其初始化为同类现有对象时,复制构造函数将被调用。
当程序生成对象副本时,编译器都将使用复制构造函数,具体地说,当函数按值传递对象或函数返回对象时,都将使用复制构造函数,或者在进行三个对象相加时,编译器也可能生成临时对象储存中间值并调用复制构造函数。
默认复制构造函数的功能
默认复制构造函数逐个复制非静态成员(成员复制也成为浅复制),复制的是成员的值。
如果类成员中使用了new初始化的指针成员,应当定义一个复制构造函数,以复制指向数据,而不是指针,这被成为深复制。

初始化对象时有两种可能:
第一种是不通过赋值运算符,而是通过复制构造函数创建对象;
第二种是通过复制构造函数创建临时对象,再将临时对象通过赋值运算符复制。
总之,初始化对象一定会调用复制构造函数,而默认复制构造函数和默认赋值运算符进行的都是浅复制,所以要显示定义复制构造函数和赋值运算符,将new生成的指针部分进行值复制而不是地址复制。

十二、运算符重载

只能通过成员函数重载的运算符:

=
()
[]
->

十三、new

new的作用是为指针分配内存;
在构造函数中使用new,就必须在析构函数˙中使用delete;
如果有多个构造函数,就必须以相同的方式使用new,因为只有一个析构函数。

十四、返回对象

通常使用const引用 返回对象效率更高,因为返回值会调用复制构造函数;
两种返回非const引用对象的情形是:重载运算符和重载cout<<,前者目的在于方便赋值,后者是必须这样做,以便连续输出。
什么情况下返回对象(不带引用)呢?就是在返回值为局部变量时需要使用返回对象。
返回const对象:不希望返回值被修改。好像一般较少使用。

十五、转换函数

将值转换为类,只需要定义一个只有一个参数的构造函数;
将类转换为值,需要定义类成员函数,operator type_name();

十六、派生类构造函数

构造函数使用初始化列表可减少一个步骤,速度更快。
派生类构造函数举例:

RatedPlayer::RatedPlayer(unsigned int r,const TableTennisPlayer& tp)
                                    :TableTennisPlayer(tp),rating(r)
{

}

十七、虚函数(虚方法)

虚函数允许基类和派生类使用各自的方法,前提是使用指针或引用传递函数。
在派生类中,通常使用作用域运算符调用基类方法。

基类指针可以指向派生类对象,因此可以创建基类指针数组,用来将基类对象和派生类对象储存在一个数组中【499页】。

虚析构函数:
为什么需要虚析构函数?因为有了虚析构函数就可以调用基类和派生类各自实现的虚构函数了。

十八、访问控制

有三种访问控制方式:Public(公有)、Private(私有)、Protected(保护)。
Public:可以在类外使用作用域运算符调用;
Private:只能通过成员函数调用;
Protected:正常情况下和Private一样,只有在派生类中有区别,对于派生类来说,保护成员和公有成员一样,好像还不用通过作用域运算符而直接调用?

十九、抽象基类(ABC类)

抽象基类又称ABC类,指的是至少使用一个纯虚函数的接口。

二十、什么不能被继承?

构造函数
析构函数
赋值运算符=
友元

二十一、继承和包含

继承有:公有继承,私有继承和保护继承;
公有继承,除了二十提到的之外都能继承,包括接口(数据成员)和实现(方法);
私有继承,私有继承和包含具有同样的效果,基类的私有成员变成不可访问,基类的公有成员和保护成员变成私有成员,就是说只有派生类的成员函数才可以访问他们;
保护继承,派生类的公有成员和保护成员都将变成保护成员;
包含,包含继承实现但不继承接口。
--- --- ---
包含和私有继承的区别:
大多数情况下使用包含而不是私有继承来表示has-a的关系,
使用私有继承的两种情况,

  1. 访问基类的保护成员;
  2. 需要重新定义虚函数。

二十二、this指针

this指针指向调用该方法的对象,举个例子:
加入Student是类,Name()是类的方法,则:

const string& Student::Name() const
{
    return (const string&) *this;
}

其中,this代表Student。

二十三、使用using重新定义访问权限

说明,派生类内指的是类的定义内,派生类外指的是对象调用,同时,该方法只适用于继承不适用于包含。
假设派生类没有特别书写一个调用基类的方法,则可以使用using重定义基类的访问权限。例如:

class Student: private std::string,private std::valarray<double>
{
...
public:
    using std::valarray<double>::min;
    using std::valarray<double>::max;
...
};

则可以这样使用:

std::cout<<"the max is:"<<ada[i].max()<<std::endl;

二十四、关于继承和初始化列表

继承和初始化列表都使用冒号:
在class后的是继承,在构造函数后的是初始化列表。
```c
//继承
class Abc():public std::string
{
private:
int aaa;
};

//初始化列表
Abc::Abc():aaa(5)
{}
```

二十五、类模版

模版不是类,模版成员函数也不是成员函数,因为他们不参加编译,只有实例化后才参与编译。因此通常将模版声明和模版定义放在同一个.h文件,使用的时候调用头文件即可。
模版是重载的一种形式,只有类型需要替换而其内部代码不变。
模版累可用作基类,也可用作组件类,还可以用作参数。
模版可以使用多个参数,可以是抽象参数,也可以是具体参数。
模版可以作为类的成员。

二十六、友元

友元类
不只是函数可以作为友元(友元函数),类也可以作为友元(友元类),举个栗子:
电视机TV和遥控器Remote,
电视机和遥控器是两个独立的类,
但是遥控器可以改变电视机的状态,如换台,调音等,
因此遥控器是电视机的一个友元类,
友元声明可以位于公有,私有或者保护部分,
友元类也是单独的类,但是具有访问原类数据的权限,就是说两个类共享一组数据。

//.h文件
class TV
{
public:
    friend class Remote;
    ......

};

class Remote
{
private:
    int mode;
    ......
};
//.c
int main()
{
    Remote grey;
    grey.volup(50);
}

如果只让Remote类的一个方法成为友元成员,需要使用前向声明小心排列声明顺序(P606)

二十七、嵌套类

嵌套类的问题在于初始化。
解决方法是在调用嵌套类的方法中初始化嵌套类。

class Queue
{
    class Node
    {
    public:
        Item item;
        Node* next;
        Node(cosnt Item& i):item(i),next(0){}
    };
    ......
    
    bool Queue enqueue(const Item& item)
    {
        //简洁初始化方式
        Node* add=new Node(item);
        ......
        //另一种初始化方式
        Node* add=new Node;
        add->item=item;
        add->next=NULL;
    }
...
};

转载于:https://www.cnblogs.com/chendeqiang/p/11442344.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值