纸上谈兵:c++细节

作者:TramiDu 出处:http://blog.csdn.net/rollerman_1 转载请保留这段声明。


本系列的博客主要是自己在学习<<数据结构与算法分析c++描述第三版>>时的一些笔记,其中大量的源码也均出自此书;


C++细节:C++可谓博大精神,在深藏技巧的同时,必然也隐藏了众多的陷阱,鉴于笔者也只是初涉C++不久,不敢妄言,首先得从概念谈起,类与对象,类的实例化就是对象,类由成员构成,成员可以是数据成员也可以是成员函数,最普通的类一般包含public和private类成员,public中的成员可以被任何类中的任何方法访问,private只能由它所在类的方法访问,数据成员声明为private的属性,从而实现了信息隐藏。构造函数是描述如何构建类的实例化的方法,如果没有显示的构造函数,就会自动根据默认值初始化数据成员。


基本概念还包括了-- 默认参数:初始化列表:explicit构造函数:常量成员函数:接口与实现的分离:预处理命令

class IntCell
{
    public:
        explicit IntCell(int initValue = 0)
            :value(initValue){}
        int read() const
        { return value; }
        void write(int x)
        { value = x; }

    private:
        int value;
};

默认参数:在IntCell的构造函数中接受一个initValue的参数,也相当于隐士的声明了一个零参数的构造函数,默认值0意味如果没有确定的参数,那么就是用0,同时默认参数可以在任何函数中使用,但是最普遍的是在构造函数中使用。

始化列表:初始化列表用来直接初始化数据成员,常使用在const的数据成员(这种数据成员初始化列表是必须的),其次就是当数据成员是类类型的时候,使用初始化列表可以提高效率,减少数据的拷贝,还有需要注意的是初始化列表的顺序必须与数据成员顺序相同。
explicit构造函数:避免隐式类型转换,这里一般单参构造函数会根据给定的值创建一个临时变量,从而使赋值变得兼容,explicit是明确不进行隐式类型转换的,所以如果你试图去做下面的操作会报错:IntCell obj; obj = 3;假如你的构造函数不是explicit那么可以编译通过,中间产生隐式类型转换以及临时变量。

常量成员函数:常量成员函数包含两类,不改变对象状态是访问函数;修改对象状态就是修改函数。默认情况下所有的成员函数都是修改函数,如果在函数后面添加const关键子那么编程访问函数。这里const关键字的解析可以移步c++ primer中的this指针。

接口与实现的分离:接口一般包括数据成员以及成员函数,一般放在.h文件中,实现一般在同名文件的.cpp文件中,从而实现接口与实现的分离,为了避免接口的反复读取,从而定义了预处理的命令#ifndef A_H #define A_H #endif。 

vector与string与C++内置数组比较:C++中的内置数组实际存储的是一个指针常量,内置数组不能进行=复制,不能记忆存储的size,同样内置数组一旦申请大小,变不能修改,灵活性很差,同样内置的字符串只是一个字符数组,==也是不能进行比较字符串的大小等。

-------------------------------------------------------------------------------------------------------------------------------------------------------

三大函数:析构函数、复制构造函数、operator=

C++中随着类默认出现的是三大函数,析构、复制构造函数以及opeartor=,众多的情况下这是编译器所默认的,但有的时候就是不行的。

析构函数:主要是释放对象所占的资源。

复制构造函数:主要是构造新对象,初始化化为相同类型对象的一个副本,例如IntCell B = C; IntCell B(C);

operator=:作用与两个已经构造的对象时,就调用=运算符依次将=右边的对象的数据成员赋值给左边的对象的数据成员;

默认值问题:一般的数据成员类似与int vector<int> vector<string> string 都接受默认值,所以这种情况就可以放心使用默认的三大函数,但是一旦数据成员中出现了指针,那么问题就来了,指针的复制:一般的复制构造函数和operator=并不复制指针所指向的对象,而是简单的赋值指针的值,这样一来得到了两个类的实例,但是它们包含的指针都是指向同一个对象的,这也就是浅复制,而一般情况是需要深复制的,即复制其指针也复制指针所指向的对象。避免默认值带来的问题一般是需要实现析构函数以及operator=这两个操作。

下面这个例子是当默认值不可用的时候,数据成员有指针类型,并且数据成员是通过new分配的内存地址:

#include<iostream>
using namespace std;
class dateManage
{
    public:
       explicit dateManage(int initvalue = 0);
       dateManage(const dateManage &ths);//复制构造函数的实现
        ~dateManage();

        const dateManage & operator=(const dateManage &ths);//重载=运算符,实现两个对象的复制
        int read() const
        {return *date;}
        void write(int x)
        {  *date = x;}
    private:
        int *date;

};
dateManage::dateManage(int initvalue)
{
    date = new int(initvalue);
}
dateManage::dateManage(const dateManage &ths)
{
    date = new int (*ths.date);//实现上只是通过将形参的date所指向的对象来初始化实参date;
}
const dateManage & dateManage::operator=(const dateManage &ths)//这里提到了一个const &(常量成员引用,作为形参传递稍后讲)
{
    if(this != &ths)
        *date = *ths.date;
    return *this;
}

dateManage::~dateManage()
{
    delete date;
}

------------------------------------------------------------------------------------------------------------------------------------------------------

参数传递以及返回值传递

参数传递的原则:按值调用适合于不被函数更改的小对象;按常量引用调用适合于不被函数更改的大对象(一般是类对象);引址调用适合与所有可以被函数更改的对象。

int avg(const vector<string> &arr, int n, bool &err) arr是vector<string>类型的,通过常量引用调用传递参数,n 是int类型通过按值调用来传递,err是引址调用来传递参数。

返回值传递:对象的返回值可以按值返回也可以是按常量引用返回,按常量引用返回的需要注意的是:返回值必须在函数结束的时候返回一个有效的对象,也就是说返回的对象必须不是一个局部变量,那么一般可以形参或者其他的数据成员,下面的这个例子就返回形参变量。

#include<iostream>
#include<vector>
using namespace std;
const string &findMax (vector<string> &arr)
{
    int maxIndex = 0;
    for(int i = 1; i < arr.size();i++){
    if(arr[maxIndex] < arr[i])
        maxIndex = i;
    }
    return arr[maxIndex];
}
int main()
{
    vector<string> vec={"ab","bc","cd"};
    string x = findMax(vec);
    cout << x << endl;
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值