一、C语言转型C++
1、C—>C++
//C类型
typedef struct STASH
{
int size; //每个内存块大小
int quantity; //存储空间数
int next; //下一个空的空间
unsigned char *storage; //存储器
}Stash;
void InitStash(Stash *s, int size);
void CleanStash(Stash *s);
int Add(Stash *s, int element);
void* fetch(Stash *s, int index);
int count(Stash *s);
void inflate(Stash *s, int increase);
//C++类型
struct Stash_Cpp //C++添加成员函数不增加结构体的空间大小,只计算变量的大小总和
{
int size; //每个内存块大小
int quantity; //存储空间数
int next; //下一个空的空间
unsigned char *storage; //存储器
void InitStash( int size);
void CleanStash();
int Add(int element);
void* fetch(int index);
int count();
void inflate(int increase);
};
特点:将函数和数据类型集合在一个结构体内,形成一个新的数据类型,称为抽象数据类型,这种类型创建的变量称为这个类型的对象或实例,用对象调用成员函数就是向对象发消息。故面向对象的设计主要活动就是向对象发消息。
结构格式:
class(struct) 类名
{
public: //共有类
protected: //保护类
private: //私有类
//成员属性
//成员函数
};
实例化对象:类名 a; //a 就是实例化对象
2、边界限定
因C语言对结构体的数据处理是没有边界限定的,C++引进了边界概念,让数据更加安全操作。所以引进三个关键字:public、private、protected。
2.1、public 同C语言的struct功能,对所有成员都可以取用,对外不做限制。
2.2、private 类的内部成员函数调用。不能通过类的实例变量访问,但可以通过友元类和友元函数访问。
2.3、protected 和private基本类似,只有一点不同:继承的结构可以访问protected,不能访问private。
3、初始化和清除
3.1、通过构造函数初始化每个对象。如果类已经有构造函数,编译器创建对象时自动调用构造函数。命名规则:和类名字相同,没有返回值。
3.2、用析构函数确保清除。如果类存在析构函数,编译器会在对象销毁的时候自动调用析构函数。命名规则:和类名称相同,没有返回值和参数。在前面添加 ~。
3.3、缺省构造函数: 当类中不存在显示构造函数,就会产生缺省构造函数。存在构造函数,不存在缺省构造函数。
//构造函数和析构函数实现案例
class X
{
public:
X(); //构造函数
X(int i); //另一个构造函数
~X(); //析构函数
};
//当对象超过其定义范围,自动调用析构函数,案例如下
{
tree t(12); //这里调用构造函数
} //括号退出调用析构函数
//外面访问不到t变量
//构造集合初始化,以上面X类进行初始化
X X2[] = {X(1), X(2), X(3)};//定义三个X实例对象的集合(数组)
4、函数的重载
4.1、同样的名字,不同的参数。可以重载
//编译器会分解范围、名字、参数产生内部名称
void print(char);
void print(float);
//假设上述两个函数都是全局函数,即确定其范围,加上名称和参数
//编译器会产生内部名称 _print_char和_print_float这两个内部名称,可以进行重载
4.2、不能通过返回值进行重载
4.3、缺省函数不能重载
int fun1(int a, int b = 1){return a+b;}
int fun1(int a){return a;}
int a;
fun1(a);
//编译器报错,参数列表匹配,这两个不是重载函数
//缺省函数特点1、只有参数列表后的才参数才能缺省2、一旦使用缺省参数,这个参数后面的参数都应该缺省。
int fun1(int X, int a=1, int b=1, int c=1); //正确
int fun1(int X, int a=1, int b, int c=1);//错误
5、常量
5.1、使用const限定定义常量。常量只能被定义的时候初始化,在其他地方不能被赋值,即不能当做左操作数。
特性:c++编译器不会为const分配存储空间,把定义保存在符号表内部,如果强制加extern和取const地址,会为其分配存储空间。
建议:一个值在其声明周期内不会改变,应将其设为常量,这样程序更安全可靠
//常量案例
int main()
{
const int i = 10;//常量
i = 100; //失败,常量不能修改值
const int j = i+10; //常量可以当做右值,
}
5.2、const在指针的应用
//指向const的指针,读法:从标识符开始的地方从里向外读,
//1、const int *pInt; //读法:pInt是一个指针,他指向const int,这里表明pInt可以指向任何地方,但他指向的内容是不能被改变的
//1.1、int const *pInt; //读法:pInt是一个指针,他指向const 的int类型,和前面用法一样,为了程序可读性,应采用第一种。
//2、int d = 1; int* const pInt = &d;//读法:pInt是一个指针,指向int类型的const指针。表明这是常指针,初始化后,就只能指向初始化的地址,不能指向其他地址,但可以改变指向地址的值,
int d = 1;
int* const pInt = &d;
*pInt = 2; //正确,可以修改d的值。
int dtemp = 1;
pInt = &dtemp; //错误,常指针不能指向其他地址
const int* const pInt = &d; //常指针,指向int类型的const指针,不能通过指针修改指向的值
//3、强制类型转化会打破const的安全性
const int e = 10;
int *pInt = (int*)&e; //有效,但是这样是没意义的。
//4、常字符串
char *pChar = "ccpoy"; //指针指向常字符串,通过指针修改常字符串,程序崩溃
5.3、const在函数的应用
void t(int *pInt)
{
}
void u(const int*pcInt)//const修饰参数
{
//*pcInt = 2; //不能常量赋值
int i = *pcInt;
//int *pInt = pcInt;//不能用非const指针指向常指针
}
const char* v() //返回值是const指针
{
return "this return result is const function\n";
}
const int* const w()//返回值是const并且函数也是const
{
static int i;
return &i;
}
int main(int argc, char *argv[])
{
int x = 0;
int *pInt = &x;
const int* pcInt = &x;
t(pInt);
//t(pcInt); //const指针不能传递非const
u(pInt);
u(pcInt);
//char *pChar = v(); //返回值const不能给非const
const char*pcChar = v();
//int *pInt2 = w();//返回值const不能给非const
const int* const pcInt2 = w();
const int* pcInt22 = w();
//*w() = 1;//const类型值不能做为左值
system("pause");
return 0;
}
5.4、const在类的应用
//定义const类对象,同定义普通数据类型一样,const对象只能调用const函数和const成员变量
const int i = 1; //普通类型
const blok B(2); //类类型
//定义类的成员函数,在函数体后加const
//如果在const函数内修改非const变量的值,需要加mutable
class X
{
public:
int f() const; //声明const成员函数
int ff(); //声明非const成员函数
X(int I); //构造函数
private:
int i;
mutable int j;
};
X::X(int I) : i(I) //构造函数初始化参数表
{
}
int X::f() const //const成员函数的定义,
{
//i = 1; //错误,通过常量对象访问const函数,进而访问非const的i
j = 1; //ok mutable声明的变量可以在const函数进行修改
return i;
}
int X::ff()
{
i = 1; //ok
j = 1; //ok
return i;
}
int main()
{
X* x = new X();
x->f(); //ok 因为const成员函数内部不允许进行修改,所以可以用非const对象调用,用于获取成员变量的信息,
x->ff(); //ok
const X*ac = new X();
ac->f(); //ok
//ac->ff(); //not ok const对象只能访问const成员函数
}
5.5、volatile关键字
在编译器认知范围内,这个数据是可以改变的。如果编译器把数据读到寄存器内,并且和寄存器没有任何接触,一般不会读取这个数据并进行优化。加上volatile修饰,告诉编译器不能做这样的假设,因为可能被其他进程改变,就必须要读取该数据并不能优化。
注意:volatile的修饰的成员函数,只能通过volatile对象进行调用。
6、内联函数
定义:在类内定义的函数自动是内联函数,或者加 inline关键字放在类外定义的函数成为内联函数。
class X
{
public:
int f() {return i;} //内联函数
inline int ff(); //内联函数
private:
int i;
};
inline int X::ff()
{
return i;
}
//可以将将内联函数设计成存取器和修改器,这样可以快速节省开销
class Rectangle
{
public:
Rectangle(int W = 0, int H = 0) : Width(W), Heigth(H) {} //初始化列表
int Width() const {return width;} //read 存取器
int Width(int W) {width = W;} //set 修改器
int Heigth() const {return heigth;} //read 存取器
int Heigth(int H) {heigth = H;} //set 修改器
private:
int width, heigth;
};
7、命名控制
7.1、static关键字
一般情况下,在文件范围内定义的名字在所有编译单元是可见的。这就是外部链接,全局变量和普通函数都有外部链接的。所以是可见的。
需求:现在想控制名字的可见性,变量在该文件范围内是可见的,可以使用本文件的所有函数都可以使用它。但是在其他文件是不能看见和使用的,即只能在本文件内部使用,这里就要使用static关键字。
int a=0;//其中a是存储在静态数据区,对所有的编译单元都是可见的。
static int a=0; //a 还是存储在静态数据区,限定在本编译单元可见,即当前Cpp。
void f()
{
static a = 0;//不会改变变量的可见性,该变量只能在本函数使用,会改变变量的存储类型,将栈区存储改变成静态数据区存储
}
7.2、其他关键字
extern:改变变量供外部编译单元访问。
auto:设置变量为局部变量,几乎不用,编译器会根据上下文判断其为局部变量。
register:也是局部变量,告诉编译器将其保存在寄存器里面,因为编译器优化比我们做的好,应避免使用。
7.3、静态成员变量和静态成员函数
class X
{
public:
X(int I=0) : i(I){j=i;}
int value() const {return i;}
static int add()
{
//i++; //错误,静态成员函数不能调用非静态成员变量
return ++j; //调用静态成员变量
}
static int f()
{
//value(); //错误静态成员函数不能调用非静态成员函数
return add();//调用静态成员函数
}
int i;
static int j;
};
int X::j = 0; //类内的静态成员变量初始化
int main()
{
int ret;
X x;
X *px = &x;
ret = x.j;
ret = px->j;
ret = X::j;
ret = x.i;
ret = px->i;
//ret = X::i;//非静态成员变量不能通过类名调用
x.value();
px->value();
//X::value();//非静态成员函数不能通过类名调用
x.f();
px->f();
X::f();
return 0;
}
说明:
1、静态成员和静态成员函数都可以被类对象调用或者通过类名::调用。
2、非静态成员和非静态成员函数不能通过类名::调用,可以通过类对象调用。
3、静态成员函数只能调用静态成员变量,不能调用非静态成员变量。
7.4、C++链接C库函数
#ifndef _DISPLAYPOS_H_ //条件编译,防止重复定义 其中#ifndef/#ifdef和#endif成对存在
#define _DISPLAYPOS_H_
#ifdef __cplusplus
extern "C"{ //C++编译器连接C库函数
#endif //对应#ifdef __cplusplus
//c库函数的声明
#ifdef __cplusplus
}
#endif //对应#ifdef __cplusplus
#endif //对应上面 #ifndef _DISPLAYPOS_H_
8、引用和拷贝构造函数
//C和C++指针针对void*的区别
int *pi;
float *pf;
void *v;
v = pi;
v = pf; //C中可以编译通过,但是C++会提示错误。
//引用的示例
int x;
int &px = x; //创建引用px指向int类型变量
int & g(int &px) //函数的参数个返回值的引用
{
px++;
return px;
}