作者: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;
}