从C过度到C++
语法的升级
引用: void swp(int &a , int &b);
默认参数: void debug(const char *ptr = "------");
函数重载: int cmp(itn data1 , int data2);
int cmp(const char *str1 ,const char *str2);
堆内存: malloc() , free() -> new , delete
引用
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a = 100;
int &b = a;//引用的用法,最终打印出来无论是值还是地址都一样。
printf("a = %d\n" , a);
printf("b = %d\n" , b);
printf("add a = %p\n" , &a);
printf("add b = %p\n" , &b);
return 0;
}
//打印输出
a = 100
b = 100
add a = 0xbffc9bd8
add b = 0xbffc9bd8
默认参数
void debug(const char *ptr = "---------------"){
printf("%s\n" , ptr);
}
int main(int argc, const char *argv[])
{
debug();
debug("hello");
return 0;
}
//打印
---------------
hello
重载
可以使用同样的函数名,但是要求参数一定不能一样
#include"stdio.h"
#include"string.h"
int cmp(int a , int b){
return a-b;
}
int cmp(const char *str1 , const char *str2){
return strcmp(str1 , str2);
}
int main(int argc, const char *argv[])
{
printf("%d\n" , cmp(1,2));
printf("%d\n" , cmp("aaaaa","bbbb"));
return 0;
}
linux@linux:~/c++$ ./a.out
-1
-1
malloc
#include"stdio.h"
#include <malloc.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//申请一个堆空间
int *intp = new int;
*intp = 100;
printf("int*p = %d\n" , *intp);
delete intp;
//申请多个堆空间
char *p = new char[10];
strcpy(p , "hello");
printf("p: %s\n" , p);
delete [] p;
return 0;
}
linux@linux:~/c++$ ./a.out
int*p = 100
p: hello
类与对象
在C语言中,我们想要写得不错一些,体现面向对象以及封装结构体更加高级的感觉。将同一类需要的数据封装在一个结构体内。但是这仍旧在顶级工程师眼里不算厉害。
C的面向对象写法(中阶)
//arr.h
#ifndef __ARR__
#define __ARR__
typedef struct arr{
int data[100];
int tail;
}ARR;
extern void init(ARR *arr);
extern void show(ARR *arr);
extern void addtail(ARR *arr , int data);
#endif
//arr.c
#include<stdio.h>
#include "arr.h"
void init(ARR *arr){
arr->tail = 0;
}
void show(ARR *arr){
int i = 0 ;
for(;i < arr->tail ; i++)
printf("%d , " ,arr->data[i] );
printf("\n");
}
void addtail(ARR *arr , int data){
arr->data[arr->tail++] = data;
}
C的升级写法(高阶)
对象与方法需要有绑定关系。拿到对象就需要操作它的方法。
将对象中的属性与方法进行绑定。对象的方法写入static进行修饰,就可以达到无法被外部随意调动的含义。将方法以函数指针的形式写入结构体中,当创建一个对象以后,不代表我们的对象的函数指针就绑定了相对应的函数,还需要在具体地指定函数指针指向某个函数
#include<stdio.h>
#include "arr.h"
static void show(ARR *arr){
int i = 0 ;
for(;i < arr->tail ; i++)
printf("%d , " ,arr->data[i] );
printf("\n");
}
static void addtail(ARR *arr , int data){
arr->data[arr->tail++] = data;
}
void init(ARR *arr){
arr->tail = 0;
arr->addtail = addtail; //对函数指针进行具体函数的绑定,指向目标函数
arr->show = show;
}
//arr.h
#ifndef __ARR__
#define __ARR__
typedef struct arr{
int data[100];
int tail;
void (*show)(struct arr *arr);
void (*addtail)(struct arr *arr , int data);
}ARR;
void init(struct arr *arr);
#endif
//main.c
#include "arr.h"
int main(int argc, const char *argv[])
{
ARR arr;
int n =10;
init(&arr);
while(n--)
arr.addtail(&arr , n);
arr.show(&arr);
return 0;
}
C语言中的弊端:
但是如果我们在main.c中的show函数前面修改arr.tail变量的话,假如使arr.tail = 0;那么,我们最后所展示的arr会达不到我们想要的效果。也就是说这种封装方法容易出现被在某一个地方修改变量的值有可能出现重大错误,而找错误极其不容易。
1.加入修改对象成员变量的语句容易会整个修改程序执行的结果。
2.我已经找到了成员结构,确还要进行传入我本人的结构体,这不是很荒唐的事情吗?
C++引入类
默认private -> 太安全
//class.cpp
#include "stdio.h"
class A{
int a;
};
int main(int argc, const char *argv[]){
A x;
x.a = 100;
return 0;
}
//执行结果
linux@linux:~/c++/class$ g++ class.cpp
class.cpp: In function ‘int main(int, const char**)’:
class.cpp:4:6: error: ‘int A::a’ is private
int a;
^
class.cpp:10:4: error: within this context
x.a = 100;
^
看似我们的C++的程序的类类似于C的结构体,但是有很大不同,它防止你对成员直接进行修改。
class A{
int a;
void show(){
printf("----------\n");
}
};
int main(int argc, const char *argv[])
{
A x;
x.show();
return 0;
}
那么我们想要调用结构体中的方法,也是会出错误的,它太安全了,以至于没有什么用
无法调用的解决方法:
关键字
为了达到我们想要的效果:变量不可以随意修改,函数可以调用从而解决我们在C语言下的困惑,我们可以给类使用关键字
我们如果想要修改变量的值,需要在public修饰下进行封装接口才能修改我们的私有变量。在C++中我们的变量是比较另类的。你在类A的外部没有声明变量他也可以调用到a,这就体现了我的类别是可以调用自己类别的,只要你声明是我本身的类A就可以了,“A::”。
#include "stdio.h"
class A{
private:
int a;
public:
void show(){
printf("----------\n");
}
void setdata(int data){
a= data;
}
int getdata(void);
};
int A::getdata(void){
return a;
}
int main(int argc, const char *argv[])
{
A x;
x.show();
x.setdata(100);
printf("%d\n" , x.getdata());
return 0;
}
//执行结果
linux@linux:~/c++/class$ ./a.out
----------
100
构造方法
无参构造函数
在类中可以定义同名方法,也就是你构造类的对象以后会直接执行的方法。注意:它必须是public下的,否则会报错。
用于初始化行为,给我的类的成员进行初始化
//执行结果
linux@linux:~/c++/class$ ./a.out
A的同名方法被执行----------
100
//class.cpp
#include "stdio.h"
class A{
private:
int a;
public:
A(){ //构造对象以后会默认执行的函数
printf("A的同名方法被执行"); //
a = 0; //完成初始化行为
} //
void show(){
printf("----------\n");
}
void setdata(int data){
a= data;
}
int getdata(void);
};
int A::getdata(void){
return a;
}
int main(int argc, const char *argv[])
{
A x;
x.show();
x.setdata(100);
printf("%d\n" , x.getdata());
return 0;
}
有参构造函数
//执行结果
linux@linux:~/c++/class$ ./a.out
----------
0
100
//class.cpp
#include "stdio.h"
class A{
private:
int a;
public:
A(){
printf("A的同名方法被执行");
a = 0;
}
A(int data){ //有参构造函数
a= data; //
} //
void show(){
printf("----------\n");
}
void setdata(int data){
a= data;
}
int getdata(void);
};
int A::getdata(void){
return a;
}
int main(int argc, const char *argv[])
{
A x(0);
x.show();
printf("%d\n" , x.getdata());
x.setdata(100);
printf("%d\n" , x.getdata());
return 0;
}
析构函数
该函数会在对象x被销毁的时候执行。,在同名方法前面加一个’~’。
不如在创建一个malloc申请资源以后,方便释放掉资源。
//执行结果
linux@linux:~/c++/class$ ./a.out
----------
0
100
//
#include "stdio.h"
class A{
private:
int a;
public:
A(){
printf("A的同名方法被执行");
a = 0;
}
A(int data){
a= data;
}
~A(){ //析构函数
printf("PROGRAM OVER!BYE BYE!\n"); //
} //
void show(){
printf("----------\n");
}
void setdata(int data){
a= data;
}
int getdata(void);
};
int A::getdata(void){
return a;
}
int main(int argc, const char *argv[])
{
A x(0);
x.show();
printf("%d\n" , x.getdata());
x.setdata(100);
printf("%d\n" , x.getdata());
return 0;
}
C++对C的诠释
实现:1.尾部添加成员 2.打印数组成员
其中我们构建对象,一个排列数组和指向尾巴的参数。由于类的私有成员不能随意修改因此提供接口在数组尾部添加数组成员以及打印成员。
this指针:
this指针就是说,如果在一个函数参数的位置有个变量data,我们类的内部有个与data相同名字的变量的时候,我们this用来区分这两个变量的。this声明是类中的私有属性。
//arr.h
#ifndef _ARR_
#define _ARR_
class ARR{
public:
ARR():tail(0){ //初始化表初始化方法 >>方法2
// tail = 0; >>方法1
}
void addtail(int data); //类似于C的函数指针
void show(void);
private:
int data[100];
int tail;
};
#endif
//arr.c
#include<stdio.h>
#include "arr.h"
void ARR::show(void){ //是ARR结构体的共有方法
int i = 0 ;
for(;i < tail ; i++)
printf("%d , " ,data[i] );
printf("\n");
}
void ARR::addtail(int data){
this->data[tail++] = data; //当私有变量与参数崇明,使用this指针
}
//main.c
#include<stdio.h>
#include "arr.h"
int main(int argc, const char *argv[])
{
ARR arr;
arr.addtail(10);
arr.addtail(12);
arr.addtail(14);
arr.addtail(16);
arr.addtail(18);
arr.show();
return 0;
}
这样的写法显得主方法结构简洁,逻辑清晰,写出了我们心目中想要的压子。
多种构建对象方法
这里有四种构建对象的方法,其中我们执行结果发现只执行了3次A方法,而执行了4次析构函数。
其中第一种一定调用了无参数构造函数,第二次也一定调用了有参构造函数,第三次系统默认的调用有参构造函数,第四次其实是复制了一份快捷方式,他们的资源指向同一个地方。这种方式构建是很危险的。
第四种方法造成的错误
#include <stdio.h>
class A{
public:
A(){
printf("-------1");
}
A(int data){
printf("A data:%d\n",data);
}
~A(){
printf("A-------------\n");
}
};
int main(int argc, const char *argv[])
{
//called when new object is created
A *p = new A(100);
A x;
A m(100);
A y = 10;
A z = y;
return 0;
}
如果我们的构造函数是在堆空间创建一个资源,而最后y和z同时分配到一块空间。其实相当于:创建y以后我们又创建了y的快捷方式,最后我们执行结果以后会出现double free的错误操作,我们在free y的过程中,z的资源已经没有了,那么我们又对该空间进行释放。
//执行结果
A()
~A()
~A()
*** Error in `./a.out': double free or corruption (fasttop): 0x093bf008 ***
Aborted (core dumped)
//执行程序
#include <stdio.h>
class A{
public:
A(){
printf("A()\n");
p = new char [10];
}
~A(){
printf("~A()\n");
delete [] p;
}
private:
char *p;
};
int main(int argc, const char *argv[])
{
A x;
A y = x;
return 0;
}
深拷贝构造函数的实现:
为了解决上述第四种方法造成的严重问题,对于拷贝拷贝到了同一片内存空间。针对这个问题,需要实现深拷贝,而不再是仅仅创建了一个快捷方式。
这种方法不能解决y = x这种拷贝方式,需要C++重载机制,在后面会讲到。
//执行结果
linux@linux:~/c++/class/3$ ./a.out
A() //申请堆空间资源
A(const A &x) //深拷贝,拷贝时没有在同一块内存中
~A()
~A()
//2_class.pp
#include <stdio.h>
#include <string.h>
class A{
public:
A(){
printf("A()\n");
p = new char [10];
strcpy(p , "hello");
}
A(const A &x){//针对拷贝,不会指向原来地址,实现深拷贝
printf("A(const A &x)\n");
p = new char [10];
strcpy(p , x.p);
}
~A(){
printf("~A()\n");
delete [] p;
}
private:
char *p;
};
int main(int argc, const char *argv[])
{
A x;
A y = x;
return 0;
}
常成员
const不能进行数据的操作。
static属于类而不属于某个对象。
const:
const数据成员只在某个对象生存期内是常量,而对于类而言是个可变的。(static 例外)
const 修饰变量 ------------ 不能修改变量,只能在初始化的时候进行设置
const 函数 ------------ 函数内部不能进行变量修改
//执行结果
A()
b = 1000
a = 10
A()
b = 1000
a = 100
A()
b = 1000
a = 50
~A()
~A()
~A()
//const.cpp
#include <stdio.h>
class A{
public:
A(int a=50 , int data = 1000):b(data){
this->a = a;
printf("A()\n");
}
~A(){
printf("~A()\n");
}
void show() const{ //常函数
printf("b = %d\n" , b);
printf("a = %d\n" , a);
}
private:
int a;
const int b; //常成员只能在初始化中定义
};
int main(int argc, const char *argv[])
{
A x(10);
x.show();
A y(100);
y.show();
A z;
z.show();
return 0;
}
static:
static函数:基于类而不基于对象
static变量:首先必须要在类外声明变量,不基于对象的变量
//执行结果
linux@linux:~/c++/class/4_const$ ./a.out
xxxxxxxxx
xxxxxxxxx
//static.cpp
#include <stdio.h>
class A{
public:
static void func(){
printf("xxxxxxxxx\n");
}
static int data;
};
int A::data = 10; //很重要
int main(int argc, const char *argv[])
{
A a;
a.func();
A::func(); //通过类调用函数
A x;
x.data = 100;
A::data = 1000; //通过类调用数据
return 0;
}
友元 friend
使用friend关键字。
友元函数
在一个外部函数你想使用某个类的私有变量就可以用到friend关键字。作为我这个类的朋友,就可以使用我的private变量了。
//执行结果
linux@linux:~/c++/class/5_friend$ ./a.out
10 , 12 , 14 , 16 , 18 ,
18 , 16 , 14 , 12 , 10 ,
//程序段
#ifndef _ARR_
#define _ARR_
class ARR{
public:
ARR():tail(0){
}
void addtail(int data);
void show(void);
friend void rev(ARR &arr); //自己的函数操作自己的函数
private:
int data[100];
int tail;
};
#endif
linux@linux:~/c++/class/5_friend$ cat main.cpp
#include<stdio.h>
#include "arr.h"
void rev(ARR &arr){ //需要使用ARR类的私有变量
int i=0 ;
for(;i<arr.tail/2 ; i++){
int tem = arr.data[i];
arr.data[i] = arr.data[arr.tail-i-1];
arr.data[arr.tail-i-1] = tem;
}
}
int main(int argc, const char *argv[])
{
ARR arr;
arr.addtail(10);
arr.addtail(12);
arr.addtail(14);
arr.addtail(16);
arr.addtail(18);
arr.show();
rev(arr);
arr.show();
return 0;
}
友元类
现在我想要以一个例子来实现一个类B去调用另一个类A的私有变量。如果常规来想是不可能的,你甚至都不能在主方法中去打印A类的值。
但是,如果我们在A中添加B为朋友的话,那么B就可以去调用A的私有变量了。
//执行结果
linux@linux:~/c++/class/5_friend$ ./a.out
100 //class B调用到了class A的私有变量
//程序段
#include<stdio.h>
class A{
public:
A(){
x = 100;
}
friend class B;
private:
int x;
};
class B{
public:
void printfA(A &x);
};
void B::printfA(A &x){
printf("%d\n" , x.x);
}
int main(int argc, const char *argv[])
{
A a;
//printf("%d\n" , a.x);
B b;
b.printfA(a);
return 0;
}
友元成员函数
对于上述事例而言,我的B其实只想调用A的一个x的私有变量,并不带表全部的东西都可以随意使用,这样会容易造成问题。因此我们可以只将B的printfA函数进行声明为朋友。
class A{
public:
A(){
x = 100;
}
friend void B::printfA(A &x);
private:
int x;
};