目
录
1
开始学习C++..............................................................................................................
4
1.1
C++的头文件.......................................................................................................
4
1.2
命名空间...............................................................................................................
4
1.3
更严格的类型转化................................................................................................
4
1.4
new和delete.......................................................................................................
4
1.5
内联函数...............................................................................................................
4
1.6
引用......................................................................................................................
5
1.7
函数的重载...........................................................................................................
5
2
类和对象......................................................................................................................
6
2.1
C++类成员
的保护..............................................................................................
6
2.2
C++类的本质.......................................................................................................
6
2.3
类的作用域...........................................................................................................
6
2.4
类的构造和析构函数............................................................................................
6
2.5
构造函数的初始化成员列表.................................................................................
6
2.5.1
原则:...........................................................................................................
7
2.6
拷贝构造函数.......................................................................................................
7
2.6.1
浅拷贝...........................................................................................................
7
2.6.2
深拷贝...........................................................................................................
7
2.6.3
原则:...........................................................................................................
8
2.7
常量类成员,常量对象。.....................................................................................
8
2.8
explicit.................................................................................................................
8
2.9
this指针...............................................................................................................
8
2.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
{
void
func()
{
cout
<< "demo2
func" <<
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>
using
namespace
std;
void
func(int
a
=
10)
{
printf("a
=
%d",a);
}
int
main()
{
func();
//这时候没有填写参数
return
0;
}
上面代码运行的结果是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_HH
template<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>
using
namespace
std;
class
man{
private:
char
name[100];
int
age;
public:
//共有方法
void
set_name(const
char
*s)
{
memset(name,0,sizeof(name));
if(strcmp(s,"tom")
==
0)
return;
strcpy(name,s);
}
void
set_age(int
i)
{
age
=
i;
}
char
*get_name()
{
return
name;
}
int
get_age()
{
return
age;
}
};
int
main()
{
man
m;
m.set_name("Marry");
//如果非想name改成tom,可以使用下面的方式
char
*p
=
m.get_name();
strcpy(p,"tom");
cout
<< m.get_name()
<< endl;
return
0;
}
如果一个类成员变量和一个全局变量重名,那么在类成员函数当中默认访问的是类的成员变量.
在类的内部访问全局标识,使用关键字::,表示释放全局变量或者全局函数
2.2
C++类的本质
类其实就是结构的数据成员加可执行代码,统一提供封装,继承,多态。
在类内部,没有权限限定符,默认是private
在结构内部,没有权限限定符,默认是public
一个类的案例:
编写头文件:
#ifndef
TEST_H
#define
TEST_H
class
man
{
private:
char
name[100];
int
age;
public:
man();
void
set_name(const
char
*s);
void
set_age(int
i);
const
char
*get_name();
int
get_age();
//
int
get_age()
//很有可能被编译器编译为inline了
//
{
//
return
age;
//
}
};
#endif
//
TEST_H
编写头文件的实现代码如下:
#include
"test.h"
#include
<string.h>
man::man()
{}
void
man::set_name(const
char
*s)
{
strcpy(name,s);
}
void
man::set_age(int
i)
{
age
=
i;
}
const
char
*man::get_name()
{
return
name;
}
int
man::get_age()
{
return
age;
}
类的调用的简单案例:
#include
<iostream>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
"test.h"
using
namespace
std;
int
main()
{
man
m;
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;
return
0;
}
2.3
类的作用域
类成成员变量作用域局限于类内部,类的外部是不可见。
一般不要在头文件里面定义变量。否则会出现问题。
2.4
类的构造和析构函数
构造函数名称和类的名称一致,而且没有返回值,在一个类实例化为一个对象的时候,自动调用。
如果没有写构造函数,会生成一个默认的构造函数和析构函数,这时候编译器会自动生成。
一个对象在销毁的时候会自动调用析构函数。
如果想传递给函数一个类的变量,为了内存消耗减小,传递的是一个类的指针。或引用
2.5
构造函数的初始化成员列表
初始化成员列表只能在构造函数使用
const成员必须用初始化成员列表赋值
引用数据成员必须用初始化成员列表赋值
案例:
编写头文件:
#ifndef
TEST_H
#define
TEST_H
class
man
{
private:
char
name[100];
const
int
age;
//如果是一个常量,必须是通过初始化常量列表的方式赋值,也就是说通过:方式赋值
//如果这里写上man和~man,这时候会出现错误。
public:
man();//构造函数的作用是初始化参数值
//重载构造函数
man(const
char
*);
man(const
char
*s,int
i);
~man();
void
set_name(const
char
*s);
void
set_age(int
i);
const
char
*get_name();
int
get_age();
void
test();
//
int
get_age()
//很有可能被编译器编译为inline了
//
{
//
return
age;
//
}
};
#endif
//
TEST_H
编写实现的代码:
#include
"test.h"
#include
<string.h>
#include
<iostream>
using
namespace
std;
//构造函数,在对象被实例化的时候调用
man::man():age(24)
//通过后面加上:的方式初始化成员变量的值
{
cout
<< "man"
<< endl;
//初始化name的值
memset(name,0,sizeof(name));
}
//构造函数的重载
man::man(const
char
*s):age(14)
{
strcpy(name,s);
}
//之所以在后面初始化值,是因为类的成员变量加了const了。
man::man(const
char
*s,
int
i):age(15)
{
cout
<< "man(const
char *s,
int i)
diao yong
le" <<
endl;
//动态分配内存,也是通过new的方式实现
//通过这种方式给成员变量赋值
strcpy(name,s);
}
man::~man()
{
//要想清楚在构造函数里分配的内存,需要在这里释放内存
//由于构造函数里只有一个,所以在不同的构造函数里面给函数成员指针分配内存的时候,一定
//要统一new或者new
cout
<< "~man"
<< endl;
}
void
man::set_name(const
char
*s)
{
strcpy(name,s);
}
void
man::set_age(int
i)
{
//age
=
i;
}
const
char
*man::get_name()
{
return
name;
}
int
man::get_age()
{
return
age;
}
void
man::test()
{
man
m;
//在栈当中将man这个类实例化为一个对象叫man
m.set_name("toto");
cout
<< m.get_age()
<< endl;
cout
<< m.get_name()
<< endl;
//cout
<<
"----重载构造函数后的参数(1个参数)----"
<<
endl;
cout
<< "--one
argumemts--"
<< endl;
//有参构造的调用
man
m2("hello");
m.set_name("toto2");
cout
<< m2.get_age()
<< endl;
cout
<< m2.get_name()
<< endl;
//cout
<<
"----重载构造函数后的参数(2个参数)----"
<<
endl;
cout
<< "----two
arguments----"
<< endl;
//有参构造的调用
man
m3("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"
using
namespace
std;
int
main()
{
man
m;
m.test();
//调用没有参数的构造函数,在堆实例化一个对象
man
*p
=
new
man();
//要写下面一句,避免出现内存泄露!!
delete
p;
//不能通过free(*p2)的方式使用
p
=
NULL;
man
*p2
=
new
man("hello",100);
delete
p2;
//不能通过free(*p2)的方式使用
p2
=
NULL;
return
0;
}
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>
using
namespace
std;
class
demo
{
public:
demo()
{
cout
<< "demo"
<< endl;
}
demo(int
i)
{
cout
<< "demo
int" <<
i <<
endl;
}
~demo()
{
cout
<< "~demo"
<< endl;
}
};
int
main()
{
//定义对象数组,同时调用带有参数的构造函数
demo
d[3]
=
{demo(1),demo(2),demo(3)};
cout
<< "Hello
World!"
<< endl;
return
0;
}
2.9
explicit
告诉C++编译器,要明确的调用这个构造函数,而不要自作聪明的认为=操作符是要调用构造的。
案例:
头文件:
#ifndef
MAN_H
#define
MAN_H
class
man
{
public:
char
*name;
int
age;
static
int
count;//定义一个类的静态成员变量,不可以进行初始化
public:
man();
explicit
man(int
age);//加了explicit之后表示就用这个构造函数。
man(const
man
&it);
man(const
char
*s,
int i
= 0);
~man();
void
set_name(const
char
*s);
void
set_age(int
i);
const
char
*get_name()
const;
int
get_age()
const;
man
*get_this();
static
void
set_count(int
i);
static
int
get_count();
};
#endif
//
MAN_H
实现类:
#include
<iostream>
#include
"man.h"
#include
<string.h>
using
namespace
std;
int
man::count
=
0;//类静态成员变量初始化的方式
man::man():age(0),
name(NULL)
{
cout
<< "man"
<< endl;
}
man::man(int
age)
{
cout
<< "man
int" <<
endl;
this->age
=
age;
}
man::man(const
man
&it)
{
cout
<< "copy
man" <<
endl;
name
=
new
char[100];
strcpy(name,
it.name);
age
=
it.age;
}
//man::man(const
char
*s)
//{
//
strcpy(name,
s);
//}
//man::man(int
i)
//{
//
age
=
i;
//}
man::man(const
char
*s,
int
i)
{
name
=
new
char[100];
cout
<< "man"
<< s
<< i
<< endl;
strcpy(name,
s);
age
=
i;
}
man::~man()
{
delete
[]name;
cout
<< "~man"
<< endl;
}
void
man::set_name(const
char
*s)
{
strcpy(name,
s);
}
void
man::set_age(int
i)
{
age
=
i;
}
const
char
*man::get_name()
const
{
return
name;
}
int
man::get_age()
const
{
return
age;
}
man
*man::get_this()
{
return
this;
}
void
man::set_count(int
i)
{
count
=
i;
//age
=
10;//类的静态函数内部不能直接访问类的动态成员变量。
}
int
man::get_count()
{
return
count;
}
main的代码
#include
<iostream>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
"man.h"
using
namespace
std;
void
test01()
{
man
m1("tom",
100);
man
m2
=
m1;//在栈当中将man这个类实例化为一个对象叫m
cout
<< "m2.name:"
<< m2.get_name()
<< endl;
m1.set_name("hello");
cout
<< "m2.name:"
<< m2.get_name()
<< endl;
}
void
test02(const
man
&m)
{
cout
<< m.get_name()
<< endl;
}
int
main()
{
//man::count
=
200;
man::set_count(200);
man
m;
printf("m
=
%p\n",
&m);
printf("%p\n",
m.get_this());
//m.set_count(500);
man
m1;
cout
<< m1.get_count()
<< endl;
return
0;
//cout
<<
"m1"
<<
m1.get_name()
<<
endl;
//
man
*p
=
new
man("hello",
100);//调用没有参数的构造函数,在堆实例化一个对象
//
delete
p;
return
0;
}
2.10
this指针
this就是指向自己实例的指针
字符串操作的案例:
头文件:
#ifndef
MYSTRING_H
#define
MYSTRING_H
#include
<iostream>
//一个单例的能够动态分配内存的字符串
class
mystring
{
private:
static
mystring
*self;
char
*s;
public:
static
mystring
*makestring(const
char
*s
= NULL);
static
void
deletestring();
~mystring();
const
char
*get_s()
const;
void
set_s(const
char
*s);
protected:
mystring();
mystring(const
char
*s);
mystring(const
mystring
&it);
};
#endif
//
MYSTRING_H
头文件的实现代码:
#include
"mystring.h"
#include
<iostream>
#include
<string.h>
mystring
*mystring::self
=
NULL;
mystring
*mystring::makestring(const
char
*s)
{
if
(self
==
NULL)
{
if
(s
==
NULL)
self
=
new
mystring;
else
self
=
new
mystring(s);
}
return
self;
}
void
mystring::deletestring()
{
if
(self
!=
NULL)
{
delete
self;
self
=
NULL;//释放指针之后,赋值NULL,这样就可以再次建立类的实例
}
}
mystring::mystring():
s(NULL)
{
}
mystring::mystring(const
char
*s)
{
int
len
=
strlen(s);
this->s
=
new
char[len
+
1];
strcpy(this->s,
s);
this->s[len]
=
0;
}
mystring::mystring(const
mystring
&it)//通过拷贝构造实现深拷贝,避免成员变量指针赋值导致的错误
{
int
len
=
strlen(it.get_s());
this->s
=
new
char[len
+
1];
strcpy(this->s,
it.s);
this->s[len]
=
0;
}
mystring::~mystring()
{
delete
[]s;//将构造函数分配的内存释放
}
const
char
*mystring::get_s()
const
{
return
s;
}
void
mystring::set_s(const
char
*s)
{
if
(this->s
==
NULL)
{
int
len
=
strlen(s);
this->s
=
new
char[len
+
1];
strcpy(this->s,
s);
this->s[len]
=
0;
}else
{
int
len1
=
strlen(this->s);
int
len2
=
strlen(s);
if
(len1
>
len2)
{
strcpy(this->s,
s);
this->s[strlen(s)]
=
0;
}else
{
delete
[]this->s;//由于成员变量s的空间不够了,所以不要了
this->s
=
new
char[len2
+
1];//重新给成员变量s分配新空间
strcpy(this->s,
s);//给新空间赋值
this->s[len2]
=
0;//新空间最后一个字节为字符串结束标示符0
}
}
}
主函数的实现代码:
#include
<iostream>
#include
"mystring.h"
using
namespace
std;
int
main()
{
//
mystring
str1("hello
world");
//
mystring
str2
=
str1;
//
str3.set_s("SDFSD");
//
cout
<<
str1.get_s()
<<
endl;
//mystring
*str1
=
mystring::makestring();//默认调用的是NULL
mystring
*str1
=
mystring::makestring("hello
world");//默认调用的是NULL
cout
<< str1->get_s()
<< endl;
mystring::deletestring();
mystring
*str3
=
mystring::makestring("aaaaaaa");
cout
<< str3->get_s()
<< endl;
return
0;
}
2.11
类的static成员变量
static变量是放到静态内存区的,程序加载就存在,一直到程序退出才清理。
类的static成员和类的对象没有直接关系,类的静态成员是放到静态内存区的,程序开始执行就存在,一直到程序结束才清理。
类的静态成员变量不论类的实例有多少,但成员变量只有一份。
单例的一个案例:
编写头文件:
#ifndef
SINGLE_H
#define
SINGLE_H
class
single
{
private:
static
single
*p;
protected:
//构造函数被保护
single();
public:
//通过方法的方式实现生成实例
static
single
*makesignle();
static
void
releasesingle();
};
#endif
//
SINGLE_H
单例的实现代码:
#include
"single.h"
#include
<iostream>
single
*single::p
=
NULL;
single::single(){}
single
*single::makesignle()
{
if
(p
==
NULL)
//如果p为空,就实例化对象返回,否则直接单例
p
=
new
single;
return
p;
}
void
single::releasesingle()
{
delete
p;
p
=
NULL;
}
main实现类
#include
<iostream>
#include
"single.h"
using
namespace
std;
//实例化单例的例子
int
main()
{
single
*p
=
single::makesignle();
single
*p1
=
single::makesignle();
single::releasesingle();
cout
<< "Hello
World!"
<< endl;
return
0;
}