第三十一讲 class(1)

         C语言部分我们总算是结束了,不过这对我们来说却不是终点,所以现在我们又来继续我们新的征程——C++,原本打算是说一些win32编程基础的,但想了下,顺应大家的号召,还是先来说说C++吧。
不管我们学习什么语言,我们一开始所见到都是hello world,所以我们今天还是从hello开始。
————————————–
#include <iostream>int main(int argc,char **argv)
{
std::cout<<“Hello World”<<std::endl;

return 0;

}
—————————————
虽然我不是我们今天的hello world和我们在一开始的hello world到底哪个具有优势一些,但是我想说,cout和printf比起虽然性能上差了一些,但是用起来却方便了不少了,关于cout和printf大家想要了解得更多,可以自己去google一下,这里我就不多说了。
同样,C++的程序入口依然main,main的参数也一样,第一个参数argc表示传递给函数的实参个数,argv则具体的实参,通常如果我们打印*argv(std::cout<<*argv<<std::endl;

),我们会得到我们程序的全部路径甚至包括exe文件名,同样,如果我们想要打印**argv(std::cout<<**argv<<std::endl;

)的话打印出来的则是我们的文件所在的盘符,如果在D盘就会打印D。
和C比起,C++的标准输出不再使用printf函数,当然如果你们习惯那样用,没问题,C++里面的标准输出去通常都是使用流,这里使用的std::cout不是函数,而是流对象,而位移操作符<<相当于流槽,所以我们可以这样来理解,当我们把东西扔进流槽后,自然会通过对象cout流出来,如果大家细心,会发现我们的预编头文件不再是stdio.h,而是iostream,而且我们并没有加上后缀.h。
cout读作C-out,那么和他相对的是不是就是C-in呢?不错,在C++里我们使用cout来输出,使用cin来输入,看下面。
————————————-
#include <iostream>#include <string>using namespace std;

int main(int argc,char **argv)
{
string str;

cin>>str;

cout<<str<<endl;

return 0;

}
————————————–
当我们输入Hello的时候回车时候就会从cout里面输出了Hello,大家是不是发现这次的写法为什么不像上次那么复杂了呢?因为我们使用using namespace std这句话,所以我们不必再纠结每次想要使用cout的时候都得加上std::,std表示名称空间,::是限制于,std::整个则是表示名称空间std里面的什么什么,至此,是不是发觉在C语言里面很多模棱两可的东西在C++里面变得清晰了呢?C语言里面我们很难做到名字的不重复,这导致用起来的时候不知道用的是哪个,但是这个问题在C++里面得到了解决,我们可以用过名称空间来避免名字的重复,就好比,我在MyStd里面有个对象叫cout,我使用的时候我就会这样用MyStd::cout,那么大家就会知道我使用的cout应该是一个只有我自己能够知道的对象,而不是我们拿来输出的cout对象,当然这个例子太坑爹,因为不会有人取一个和std里面同名的名字的来作为自己私有的对象。
尽管C++的语法在C部分我们说得差不多了,但是对于引用我们不得不说,关于引用,我们通过一个例子来阐述:
——————————————
//reference.cpp
#include <iostream>using namespace std;

double cube(double a);

double refcube(double &

ra);

int main()
{
double x = 5.0;

cout<<cube(x)<<endl;

cout<<x<<endl;

cout<<refcube(x)<<endl;

cout<<x<<endl;

return 0;

}
double cube(double a)
{
a *= a*a;

return a;

}
double refcube(double &

ra)
{
ra *= ra*ra;

return ra;

}
————————————
运行程序将会输入如下结果:
125
5
125
125
可能大家都觉得奇怪,为什么引用会将原始数据给修改了呢?这就是引用的神奇之处,为了解决问题,我们不得不使用const,所以,大家通常都会发现,在出现引用的同时几乎都会带上const同行。所以我们可以修改上面的程序
———————————–
double refcube(const double &

ra)
{
double x =ra* ra*ra;

return x;

}
———————————-
C++的灵活多变从一个const的使用中便可见一斑,很多时候,尤其对于那些一知半解的同学来说,const足以令他不知所措,比如:
———————–
1)const double * a;
2)double const * a;
3)double * const a;
4)double *a const;
——————————
这些种种不同的书写方式代表着不同的操作,不过其中第一个和第二个表示同一种操作,第三种和第四种表示同一种操作方式。
在编写程序中,应尽量多使用const,他不但可以避免无意中修改数据的编程错误,还可以使函数能够处理const和非const的实参,重要的是const引用使函数能够正确的生成并使用临时变量。这就是我们上面可以使用临时变量x来传递值的原因。
说了这么多,那么引用到底是什么东西?
————————————————-
int &

a   //a就是int的引用,也就是说a就是int,不过通常情况对于这些基本类型是不使用引用,引用通常都是用于一些自定义的类型。
————————————————
现在我们应该对引用有一定要的了解了,记得当时第一次看到引用的时候我还纳闷了下,我在想怎么这里突然要指针了呢(对变量取地址也就是获取他的指针),于是后来又看到一个诸如&

&

a之类的东西,我更纳闷了,心想,这不是指针的指针了吗?在后来,才发现,原来如果只有一个&

表示的是左值引用,而两个&

的则是表示右值引用,当然右值引用的情况我们很少碰到,但是不表示不存在(关于左值和右值我不想说得太多,很多人一直无法区分何为左值何为右值,我的判断比较简单,在‘=’左边的就当他是左值,在右边的自然就是右值了,不过要真正区分起来要比这复杂得多,所以大家如果想要深究的话可以多差下相关书籍)。
现在我们来看下class,C++属于面向对象的语言,主要的功劳就是class了,所以让我们来看看怎么设计一个类,假如我们想要得到一个表示日期的类型,如果是以前我们会考虑使用struct。我们一定会这样写:
———————–
struct Date{
  
int d,m,y;


};



void init_data(Date &

d,int,int,int);



void add_year(Date &

d,int n);



void add_month(Date &

d,int n);



void add_day(Date &

d,int n);


——————————
这种写法明显有个缺陷,我们定义的日期类型和和这些函数之间没有显示关系,我们只能通过参数去使用,这显得他们不是一个整体,我们可以这样修改:
——————————-
struct Date{
  
int d,m,y;


  
void init_date(int dd,int mm,int yy);


  
void add_year(int n);


  
void add_month(int n);


  
void add_day(int n);


}
——————————————–
我们都知道在C语言中,struct里面是不能带函数的,但是如今我们说的是C++,所以可以把函数放在struct里面,然后使用的时候就方便得多。
——————————
Date my_birthday;



void f()

{
     Date today;


     today.init_date(02,26,2014);


     my_birthday.init(06,19,1987);


     Date  tomorrow = today;

    
     tomorrow.add_day(1);


     //……
}
————————————
在外面定义这些声明的函数时,我们记得添加限制域“::”:
————————————-

void Date::Init(int dd,int mm,int yy)

{
       d = dd;


       m = mm;


       y = yy;


}
—————————————–
这就是我们通常的初始化方式。当我们使用Date的对象today调用这个初始化函数时,d = dd将会把值赋给today.d,同样y = yy会把值赋给today.y,m = mm会把值赋给today.m。
struct是一种类型,而类的对象知道自己调用的是那个函数,所以,当他调用Init之后,today的值就是我们今天的日期,而调用后面的add_day(1)之后就是明天了,所以想要得到明天,我们就先把今天赋给明天,再让明天+1就是真正的明天了。
但是在C++里面要定义一个类,我们同时使用class。所以我们可以用class来改写上面的声明:
———————————–
class Date{
public:
    
void init_date(int dd,int mm,int yy);


    
void add_year(int n);


     
void add_month(int n);


    
void add_day(int n);


private:
       int d,m,y;


}
————————————-
我们把数据成员声明为似有成员,接口函数声明为公有的,这样可以实现数据的保护(保护d,m,y);
哦,对了,既然C++里面class和struct都能够表示一个类,那么他们的区别到底在哪里呢?struct的默认类型是公有的,而class则是私有的,如果我们把上面的类型换成下面的这种写法效果一样:
————————————–
class Date{
      int d,m,y;


public:
    
void init_date(int dd,int mm,int yy);


    
void add_year(int n);


     
void add_month(int n);


    
void add_day(int n);


}
———————————-
class的成员函数定义方式和stuct的一样,例如:
———————————–

void Date::add_year(int n)

{
      y += n;

//n年以后
}
————————————–
如果有非成员想要试图访问我们的似有成员,那就是不允许的:
———————————
void  f(Date &

d)
{
         d.y += 100;

//y是Date私有成员,禁止访问
}
———————————-
现在我们重新回来看看我们的class,如果我们的class里的初始化和struct一样的话,干嘛要出一个class呢?class的优势就是因为他比struct多了一个构造函数和一个析构函数。我们把所有的初始化都放在构造函数里,同时把所有资源的释放都放在析构函数。
那么什么事构造函数呢?构造函数就是和类型名相同的一个函数,而析构函数则是在构造函数前面添加一个“~”号即可,不需要任何参数,如下:
—————————————-
class Date{
      int d,m,y;


public:
      Date(int,int,int);//构造函数,也就是初始化,日,月,年。
     Date(int,int);//构造函数,初始化日,月,年是本年。
     Date(int);

//构造函数,初始化日期,本月本年。
     Date();

//构造函数,默认日期,也就是今天。
     Date(const char*);

//构造函数,用字符串表示日期。
      ~Date();

//析构函数
……
}
—————————————
我们添加了各种类型的构造函数,以便在使用的时候做到随心所欲而不会出错。
———————————————-
Date  today(26);

//编译通过查看头文件,然后再查找定义,最后会给返回今天的日期来(26/2/2014)。
Date today(“2月26日2014年“);

//编译器会通过查找class找到和他对应的原型Date(const char*)出来,最后返回今天的日期。
————————————————-
今天就先到这里吧,如果说以前的C说得杂乱无章,现在我们的C++将会有体系的进行下去,所以明天的任务还会是我们今天的Date。
由于我们C已经说了三十讲,关于C的基础已经说得差不多,同时这些基础一样是C++的基础,所以至于C++语法这一块我就不再重复了,接下来要说的会越来越深,如果大家觉得难以理解,现在提出来,我考虑从相对简单一些的基础开始。

发表评论