您的位置:首页 > 编程语言 > C语言/C++

C++文件头,命名空间,new和delete,内联函数,引用,函数重载,构造函数和析构函数,深拷贝和浅拷贝,explict,this指针

2014-08-09 18:49 856 查看
 目 录1 开始学习C++..............................................................................................................41.1C++的头文件.......................................................................................................41.2命名空间...............................................................................................................41.3更严格的类型转化................................................................................................41.4new和delete.......................................................................................................41.5内联函数...............................................................................................................41.6引用......................................................................................................................51.7函数的重载...........................................................................................................52 类和对象......................................................................................................................62.1C++类成员的保护..............................................................................................62.2C++类的本质.......................................................................................................62.3类的作用域...........................................................................................................62.4类的构造和析构函数............................................................................................62.5构造函数的初始化成员列表.................................................................................62.5.1原则:...........................................................................................................72.6拷贝构造函数.......................................................................................................72.6.1浅拷贝...........................................................................................................72.6.2深拷贝...........................................................................................................72.6.3原则:...........................................................................................................82.7常量类成员,常量对象。.....................................................................................82.8explicit.................................................................................................................82.9this指针...............................................................................................................82.10类的static成员变量............................................................................................8

1 开始学习C++

1.1 C++的头文件

传统的C头文件。(支持.h头文件,比如:#include<stdio.h>)C++头文件。(不加.h的头文件,比如:#include<iostream>)hpp文件件。(支持.hpp头文件)在工作中如果有C的也有C++的,最好使用带有.h的头文件

1.2 操作符重载

cout << "Hello World!"<< endl;这里的”<<”实际上进行了操作符的重载。

1.3 关于使用命名空间的情况

A:使用类似:using namespace std;B:如果不使用用usingnamespace std;那么这个时候可以在代码中使用类似下面的情况:std:cout << "Helloworld\n" << endl;

1.4 命名空间

C++引入了新的概念,命名空间可以有效避免大型项目中的各种名称冲突class关键字class是C++的核心,是面向对象编程的核心内容。一个class案例:#include <iostream>#include <string.h>using namespace std; class man{public://共有的 char name[100]; private://私有的,只要下面不访问这里的age,程序就不会出现问题 int age; public: int sex;}; //注意:这里最后要有一个分号 int main(){ man m; strcpy(m.name,"tom"); m.sex = 1; cout << m.name << m.sex << endl; return 0;}自定义命名空间:

使用匿名命名空间: namespace{ voidfunc() { cout<< "demo2func" <<endl2; }}

1.5 volatile关键字

通过volatile关键字使代码不被编译器优化,案例:volatile int i=0;//保证i不被编译器优化,以便能进行中间步骤i+=6;i+=7;如果加了volatile关键字,那么就使程序不被优化成为i+=13

1.6 更严格的类型转化

在C++,不同类型的指针是不能直接赋值的,必须强转。(也就是如果两个指针类型不同,不能直接把一个赋值给另外一个,而是要通过强转的方式实现)

1.7 new和delete

c++中不建议使用malloc和free开辟内存或释放内存。而是使用new和delete。new和delete是C++内建的操作符,不需要有任何头文件,用new分配的内存必须用delete释放,不要用free。int *p=new int; 等价于:int*p=new int(10);//分配内存的同时初始化*p =10; delete p;delete p;p = NULL; new创建数组的方法new[];int *p=new int[10]; //表示开辟10个空间的数组for(int i=0;i<10;i++){p[i]=i;}//输出结果for(int i = 0;i<10;i++){ cout << p[i] << endl;}delete []p; //如果要删除这些数组的空间,要加上[],表示这时候删除的是一个数组。P = NULL;

1.8 内联函数

inline关键字的意思是,内联函数不作为函数调用,而是直接把内联函数的代码嵌入到调用的语句中内联函数适合函数代码很少,并且有频繁的大量调用。

1.9 引用

引用就是一个变量的别名,比如int a = 5;int &c = a; //这里的c就相当于是a的别名引用不是地址,虽然加上了&。

函数的缺省参数C++允许函数在定义的时候,提供缺省参数,如果调用函数的时候没有提供形参,那么形参的值就是缺省值,也就是说用默认值。#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h> usingnamespacestd; voidfunc(inta=10){ printf("a=%d",a);} intmain(){ func(); //这时候没有填写参数 return0;}上面代码运行的结果是10. 此外,函数会自动通过传递的参数类匹配调用哪个函数,案例如下:



引用做为函数的参数,没有出栈,入栈的操作,所以效率更高如果要使引用参数的值不能在函数内部被修改,那么就定义为常量引用 const &

引用例子:

1.10 函数的重载

函数的名称是一样的,但参数不同可以重载函数参数相同,但返回值不同,不可以重载

1.11 模板


A:模板的概念我们已经学过重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同。正确的调用重载函数。例如,为求两个数的最大值,我们定义MAX()函数需要对不同的数据类型分别定义不同重载(Overload)版本。//函数1.int max(int x,int y);
{return(x>y)?x:y ;}//函数2.
float max( float x,float y){
return (x>y)? x:y ;}//函数3.
double max(double x,double y)
{return (c>y)? x:y ;}但如果在主函数中,我们分别定义了 chara,b;那么在执行max(a,b);时程序就会出错,因为我们没有定义char类型的重载版本。现在,我们再重新审视上述的max()函数,它们都具有同样的功能,即求两个数的最大值,能否只写一套代码解决这个问题呢?这样就会避免因重载函数定义不全面而带来的调用错误。为解决上述问题C++引入模板机制,模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。 B:函数模板的写法函数模板的一般形式如下:Template <class或者也可以用typename T>返回类型函数名(形参表)
{//函数定义体 }说明: template是一个声明模板的关键字,表示声明一个模板关键字class不能省略,如果类型形参多余一个,每个形参前都要加class <类型形参表>可以包含基本数据类型可以包含类类型.请看以下程序://Test.cpp#include<iostream>using std::cout;using std::endl;//声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替,//T可以被任何字母或者数字代替。template <class T>T min(T x,T y){return(x<y)?x:y;}void main( ){ intn1=2,n2=10; doubled1=1.5,d2=5.6; cout<<"较小整数:"<<min(n1,n2)<<endl; cout<<"较小实数:"<<min(d1,d2)<<endl; system("PAUSE");}程序运行结果:

程序分析:main()函数中定义了两个整型变量n1 , n2 两个双精度类型变量d1 , d2然后调用min( n1,n2); 即实例化函数模板T min(Tx, T y)其中T为int型,求出n1,n2中的最小值.同理调用min(d1,d2)时,求出d1,d2中的最小值.C:类模板的写法定义一个类模板:Template < class或者也可以用typename T >
class类名{
//类定义......
};说明:其中,template是声明各模板的关键字,表示声明一个模板,模板参数可以是一个,也可以是多个。例如:定义一个类模板:// ClassTemplate.h
#ifndef ClassTemplate_HH#define ClassTemplate_HHtemplate<typename T1,typename T2>class myClass{private: T1 I; T2 J;public: myClass(T1 a, T2 b);//Constructor void show();};//这是构造函数//注意这些格式template <typename T1,typename T2>myClass<T1,T2>::myClass(T1 a,T2 b):I(a),J(b){}//这是voidshow();template <typename T1,typename T2>void myClass<T1,T2>::show(){ cout<<"I="<<I<<",J="<<J<<endl;}#endif// Test.cpp#include<iostream>#include"ClassTemplate.h"using std::cout;using std::endl;void main(){ myClass<int,int>class1(3,5); class1.show(); myClass<int,char>class2(3,'a'); class2.show(); myClass<double,int>class3(2.9,10); class3.show(); system("PAUSE");}最后结果显示:

2 类和对象

2.1 C++类成员的保护

如果类函数返回的是成员变量的指针,为了避免在类外部成员变量被修改,所以函数就要返回常量指针#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h> usingnamespacestd; classman{private: charname[100]; intage; public: //共有方法 voidset_name(constchar*s) { memset(name,0,sizeof(name)); if(strcmp(s,"tom")==0) return; strcpy(name,s); } voidset_age(inti) { age=i; } char*get_name() { returnname; } intget_age() { returnage; }}; intmain(){ manm; m.set_name("Marry"); //如果非想name改成tom,可以使用下面的方式 char*p=m.get_name(); strcpy(p,"tom"); cout<< m.get_name()<< endl; return0;}如果一个类成员变量和一个全局变量重名,那么在类成员函数当中默认访问的是类的成员变量.在类的内部访问全局标识,使用关键字::,表示释放全局变量或者全局函数

2.2 C++类的本质

类其实就是结构的数据成员加可执行代码,统一提供封装,继承,多态。在类内部,没有权限限定符,默认是private在结构内部,没有权限限定符,默认是public一个类的案例:编写头文件:#ifndefTEST_H#defineTEST_H classman{private: charname[100]; intage;public: man(); voidset_name(constchar*s); voidset_age(inti); constchar*get_name(); intget_age();// intget_age()//很有可能被编译器编译为inline了// {// returnage;// }}; #endif//TEST_H编写头文件的实现代码如下:#include"test.h"#include<string.h> man::man(){} voidman::set_name(constchar*s){ strcpy(name,s);} voidman::set_age(inti){ age=i;} constchar*man::get_name(){ returnname;} intman::get_age(){ returnage;}类的调用的简单案例:#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h>#include"test.h" usingnamespacestd; intmain(){ manm; m.set_name("Marry"); m.set_age(20); //类的大小实际上是成员变量的大小,和去掉方法后的结构体的大小时相同的 printf("sizeof(man)=%d\n",sizeof(man)); cout<< m.get_name()<< endl; cout<< m.get_age()<< endl; return0;}

2.3 类的作用域

类成成员变量作用域局限于类内部,类的外部是不可见。一般不要在头文件里面定义变量。否则会出现问题。

2.4 类的构造和析构函数

构造函数名称和类的名称一致,而且没有返回值,在一个类实例化为一个对象的时候,自动调用。如果没有写构造函数,会生成一个默认的构造函数和析构函数,这时候编译器会自动生成。一个对象在销毁的时候会自动调用析构函数。 如果想传递给函数一个类的变量,为了内存消耗减小,传递的是一个类的指针。或引用

2.5 构造函数的初始化成员列表

初始化成员列表只能在构造函数使用const成员必须用初始化成员列表赋值引用数据成员必须用初始化成员列表赋值案例:编写头文件:#ifndefTEST_H#defineTEST_H classman{private: charname[100]; constintage; //如果是一个常量,必须是通过初始化常量列表的方式赋值,也就是说通过:方式赋值 //如果这里写上man和~man,这时候会出现错误。public: man();//构造函数的作用是初始化参数值 //重载构造函数 man(constchar*); man(constchar*s,inti); ~man(); voidset_name(constchar*s); voidset_age(inti); constchar*get_name(); intget_age(); voidtest();// intget_age()//很有可能被编译器编译为inline了// {// returnage;// }}; #endif//TEST_H 编写实现的代码:#include"test.h"#include<string.h>#include<iostream> usingnamespacestd; //构造函数,在对象被实例化的时候调用man::man():age(24) //通过后面加上:的方式初始化成员变量的值{ cout<< "man"<< endl; //初始化name的值 memset(name,0,sizeof(name));} //构造函数的重载man::man(constchar*s):age(14){ strcpy(name,s);} //之所以在后面初始化值,是因为类的成员变量加了const了。man::man(constchar*s,inti):age(15){ cout<< "man(constchar *s,int i)diao yongle" <<endl; //动态分配内存,也是通过new的方式实现 //通过这种方式给成员变量赋值 strcpy(name,s);} man::~man(){ //要想清楚在构造函数里分配的内存,需要在这里释放内存 //由于构造函数里只有一个,所以在不同的构造函数里面给函数成员指针分配内存的时候,一定 //要统一new或者new cout<< "~man"<< endl;} voidman::set_name(constchar*s){ strcpy(name,s);} voidman::set_age(inti){ //age=i;} constchar*man::get_name(){ returnname;} intman::get_age(){ returnage;} voidman::test(){ manm; //在栈当中将man这个类实例化为一个对象叫man m.set_name("toto"); cout<< m.get_age()<< endl; cout<< m.get_name()<< endl; //cout<<"----重载构造函数后的参数(1个参数)----"<<endl; cout<< "--oneargumemts--" << endl; //有参构造的调用 manm2("hello"); m.set_name("toto2"); cout<< m2.get_age()<< endl; cout<< m2.get_name()<< endl; //cout<<"----重载构造函数后的参数(2个参数)----"<<endl; cout<< "----twoarguments----"<< endl; //有参构造的调用 manm3("hello",20); m3.set_name("toto3"); cout<< m3.get_age()<< endl; cout<< m3.get_name()<< endl;} 编写main函数:#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h>#include"test.h" usingnamespacestd; intmain(){ manm; m.test(); //调用没有参数的构造函数,在堆实例化一个对象 man*p=newman(); //要写下面一句,避免出现内存泄露!! deletep; //不能通过free(*p2)的方式使用 p=NULL; man*p2=newman("hello",100); deletep2; //不能通过free(*p2)的方式使用 p2=NULL; return0;}

2.5.1 原则:

由于析构函数只有一个,所以在不同的构造函数里面给函数的成员指针分配内存的时候,一定要统一new或者new[]

2.6 拷贝构造函数

2.6.1 浅拷贝

两个对象之间成员变量简单的赋值。比如:man m1;man m2 = m1;

2.6.2 深拷贝

不同的对象指针成员指向不同的内存地址,拷贝构造的时候不是简单的指针赋值,而是将内存拷贝过来(先申请内存空间)。

2.6.3 原则:

如果类成员有指针,那么需要自己实现拷贝构造函数,不然存在浅拷贝的风险。

2.7 常量类成员,常量对象

类成员后面跟关键字const意思是告诉编译器,这个函数内部不会对类成员变量做任何修改。 函数的参数如果是一个类,那么就用类的引用。如果不想参数被调用函数内部修改,那么就采用const&

2.8 对象数组

#include<iostream> usingnamespacestd; classdemo{public: demo() { cout<< "demo"<< endl; } demo(inti) { cout<< "demoint" <<i <<endl; } ~demo() { cout<< "~demo"<< endl; }}; intmain(){ //定义对象数组,同时调用带有参数的构造函数 demod[3]={demo(1),demo(2),demo(3)}; cout<< "HelloWorld!" << endl; return0;}

2.9 explicit

告诉C++编译器,要明确的调用这个构造函数,而不要自作聪明的认为=操作符是要调用构造的。案例:头文件:#ifndefMAN_H#defineMAN_H classman{public: char*name; intage; staticintcount;//定义一个类的静态成员变量,不可以进行初始化 public: man(); explicitman(intage);//加了explicit之后表示就用这个构造函数。 man(constman&it); man(constchar*s,int i= 0); ~man(); voidset_name(constchar*s); voidset_age(inti); constchar*get_name()const; intget_age()const; man*get_this(); staticvoidset_count(inti); staticintget_count();}; #endif//MAN_H 实现类:#include<iostream>#include"man.h"#include<string.h> usingnamespacestd; intman::count=0;//类静态成员变量初始化的方式 man::man():age(0),name(NULL){ cout<< "man"<< endl;} man::man(intage){ cout<< "manint" <<endl; this->age=age;} man::man(constman&it){ cout<< "copyman" <<endl; name=newchar[100]; strcpy(name,it.name); age=it.age;} //man::man(constchar*s)//{// strcpy(name,s);//} //man::man(inti)//{// age=i;//} man::man(constchar*s,inti){ name=newchar[100]; cout<< "man"<< s<< i<< endl; strcpy(name,s); age=i;} man::~man(){ delete[]name; cout<< "~man"<< endl;} voidman::set_name(constchar*s){ strcpy(name,s);} voidman::set_age(inti){ age=i;} constchar*man::get_name()const{ returnname;} intman::get_age()const{ returnage;} man*man::get_this(){ returnthis;} voidman::set_count(inti){ count=i; //age=10;//类的静态函数内部不能直接访问类的动态成员变量。} intman::get_count(){ returncount;} main的代码#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h>#include"man.h" usingnamespacestd; voidtest01(){ manm1("tom",100); manm2=m1;//在栈当中将man这个类实例化为一个对象叫m cout<< "m2.name:"<< m2.get_name()<< endl; m1.set_name("hello"); cout<< "m2.name:"<< m2.get_name()<< endl;} voidtest02(constman&m){ cout<< m.get_name()<< endl;} intmain(){ //man::count=200; man::set_count(200); manm; printf("m=%p\n",&m); printf("%p\n",m.get_this()); //m.set_count(500); manm1; cout<< m1.get_count()<< endl; return0; //cout<<"m1"<<m1.get_name()<<endl;// man*p=newman("hello",100);//调用没有参数的构造函数,在堆实例化一个对象// deletep; return0;}

2.10 this指针

this就是指向自己实例的指针字符串操作的案例:头文件:#ifndefMYSTRING_H#defineMYSTRING_H #include<iostream> //一个单例的能够动态分配内存的字符串classmystring{private: staticmystring*self; char*s;public: staticmystring*makestring(constchar*s= NULL); staticvoiddeletestring(); ~mystring(); constchar*get_s()const; voidset_s(constchar*s); protected: mystring(); mystring(constchar*s); mystring(constmystring&it); }; #endif//MYSTRING_H 头文件的实现代码:#include"mystring.h"#include<iostream>#include<string.h> mystring*mystring::self=NULL; mystring*mystring::makestring(constchar*s){ if(self==NULL) { if(s==NULL) self=newmystring; else self=newmystring(s); } returnself;} voidmystring::deletestring(){ if(self!=NULL) { deleteself; self=NULL;//释放指针之后,赋值NULL,这样就可以再次建立类的实例 }} mystring::mystring():s(NULL){ } mystring::mystring(constchar*s){ intlen=strlen(s); this->s=newchar[len+1]; strcpy(this->s,s); this->s[len]=0;} mystring::mystring(constmystring&it)//通过拷贝构造实现深拷贝,避免成员变量指针赋值导致的错误{ intlen=strlen(it.get_s()); this->s=newchar[len+1]; strcpy(this->s,it.s); this->s[len]=0;} mystring::~mystring(){ delete[]s;//将构造函数分配的内存释放} constchar*mystring::get_s()const{ returns;} voidmystring::set_s(constchar*s){ if(this->s==NULL) { intlen=strlen(s); this->s=newchar[len+1]; strcpy(this->s,s); this->s[len]=0; }else { intlen1=strlen(this->s); intlen2=strlen(s); if(len1>len2) { strcpy(this->s,s); this->s[strlen(s)]=0; }else { delete[]this->s;//由于成员变量s的空间不够了,所以不要了 this->s=newchar[len2+1];//重新给成员变量s分配新空间 strcpy(this->s,s);//给新空间赋值 this->s[len2]=0;//新空间最后一个字节为字符串结束标示符0 } }} 主函数的实现代码:#include<iostream>#include"mystring.h" usingnamespacestd; intmain(){// mystringstr1("helloworld");// mystringstr2=str1;// str3.set_s("SDFSD");// cout<<str1.get_s()<<endl; //mystring*str1=mystring::makestring();//默认调用的是NULL mystring*str1=mystring::makestring("helloworld");//默认调用的是NULL cout<< str1->get_s()<< endl; mystring::deletestring(); mystring*str3=mystring::makestring("aaaaaaa"); cout<< str3->get_s()<< endl; return0;}

2.11 类的static成员变量

static变量是放到静态内存区的,程序加载就存在,一直到程序退出才清理。 类的static成员和类的对象没有直接关系,类的静态成员是放到静态内存区的,程序开始执行就存在,一直到程序结束才清理。类的静态成员变量不论类的实例有多少,但成员变量只有一份。单例的一个案例:编写头文件:#ifndefSINGLE_H#defineSINGLE_H classsingle{private: staticsingle*p;protected: //构造函数被保护 single(); public: //通过方法的方式实现生成实例 staticsingle*makesignle(); staticvoidreleasesingle();}; #endif//SINGLE_H 单例的实现代码:#include"single.h"#include<iostream> single*single::p=NULL; single::single(){} single*single::makesignle(){ if(p==NULL) //如果p为空,就实例化对象返回,否则直接单例 p=newsingle; returnp;} voidsingle::releasesingle(){ deletep; p=NULL;} main实现类#include<iostream>#include"single.h" usingnamespacestd; //实例化单例的例子intmain(){ single*p=single::makesignle(); single*p1=single::makesignle(); single::releasesingle(); cout<< "HelloWorld!" << endl; return0;}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐