C++之类的引入
一、结构体到类的演变
目录
- C++之类的引入
- 一、结构体到类的演变
- 二、对象的定义与使用
- 2.1、栈上定义对象
- 2.2、堆上定义对象
- 2.2.1、堆上普通数据类型的定义
- 2.2.2、堆上类对象的定义
- 三、类的大小
- 四、类的访问权限
- 五、类的存储方式
C语法
- 定义新的数据类型,该新的数据类型称之为结构体,成员为各种数据类型的变量。
struct stu
{
char age;
int greade;
};
stu stu1; //用新的数据类型结构体定义变量
C++语法
- 定义新的数据类型该新的数据类型称之为类,成员不仅可以包含变量数据,还包含着方法,也称操作函数。
class stu
{
char age;
int greade;
void operate()
{
cout<<"hell a new data is create<<endl";
}
};
stu stu1; //用新的数据类型类定义变量(C++这里的用类定义的变量称为对象)
二、对象的定义与使用
2.1、栈上定义对象
//方式1、 先声明类,再定义对象
class stu
{
char age;
int greade;
void operate()
{
cout<<"hell a new data is create<<endl";
}
};
stu stu1; //第一种定义对象的方法
class stu stuq; //第二种定义对象的方法
//方式2、声明类同时定义对象
class stu
{
char age;
int greade;
void operate()
{
cout<<"hell a new data is create<<endl";
}
}stu1;
//方式3、没有类名,直接定义对象
class
{
char age;
int greade;
void operate()
{
cout<<"hell a new data is create<<endl";
}
}stu1;
2.2、堆上定义对象
2.2.1、堆上普通数据类型的定义
C语言中提供了 malloc 和 free 两个系统函数,完成对堆内存的申请和释放。而 C++则提供了两关键字 new 和 delete。
//整形类型的定义
int main()
{
// int *p = (int*)malloc(sizeof(int));
int *p = new int(10); //new
*p = 100;
cout<<*p<<endl;
int **pp = new int *; // (int**)malloc(sizeof(int*))
Stu *ps = new Stu;
return 0;
}
//一维数组的定义
int main()
{
// new float [10] malloc(10*sizeof(float);
float *p = new float [10]{1.2,3.4};
for(int i=0; i<10; i++)
{
cout<<p[i]<<endl;
}
char **pp = new char *[11];
for(int i=0; i<10; i++)
{
pp[i] = "china";
}
pp[10] = nullptr;
while(*pp)
{
cout<<*pp++<<endl;
}
}
注意事项
1 new/delete 是关键字,效率高于 malloc 和 free。
2 配对使用,避免内存泄漏和多重释放。
3 避免,交叉使用。比如 malloc 申请的空间去 delete, new 出的空间被 free。
- 如果只是以上的功能, C中的 malloc 和 free 完全可以胜任, C++就没有必要更进一步,引入这两个关键字。重点用在类对象的申请与释放。申请的时候会调用构造器完成初始化,释放的时候,会调用析构器完成内存的清理。(见下文)
2.2.2、堆上类对象的定义
//1、new创建类对象例子:
CTest* pTest = new CTest(); //pTest用来接收类对象指针。
delete pTest;
//2、不用new,直接使用类定义申明
CTest mTest;
/*此种创建方式,使用完后不需要手动释放,该类析构函数会自动执行。而new申请的对象,则只有调用到delete时再会执行析构函数,如果程序退出而没有执行delete则会造成内存泄漏。*/
//3、只定义类指针
CTest *mTest = NULL;
mTest = new CTest();
1、这跟不用new申明对象有很大区别,类指针可以先行定义后赋值,类指针只是个通用指针,在new之前不为该类对象分配任何内存空间。
2、用普通方式创建的类对象,在创建之初就已经分配了内存空间。而类指针,如果未经过对象初始化,则不需要delete释放。
//4、new对象指针作为函数参数和返回值
// 下面要示意一下类指针对象作为返回值和参数使用
class CTest
{
public:
int a;
};
class CBest
{
public:
int b;
};
CTest* fun(CBest* pBest)
{
CTest* pTest = new CTest();
pTest->a = pBest->b;
return pTest;
}
int main()
{
CBest* pBest = new CBest();
CTest* pRes= fun(pBest);
if(pBest!=NULL)
delete pBest;
if(pRes!=NULL)
delete pRes ;
return -1;
}
- new创建对象的特点:
1、new创建类对象需要指针接收,一处初始化,多处使用
2、new创建类对象使用完需delete销毁
3、new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间
4、new对象指针用途广泛,比如作为函数返回值、函数参数等
5、频繁调用场合并不适合new,就像new申请和释放内存一样
- new与非new作用域
1、不用new:作用域限制在定义类对象的方法中,当方法结束时,类对象也被系统释放了。(安全不会造成内存系统泄漏)。
2、用new:创建的是指向类对象的指针,作用域变成了全局,当程序结束时,必须用delete删除,系统不会自动释放,(不注意可能造成内存泄漏)。
三、类的大小
备注:以下内容摘自:
1.空类
class A
{
};
sizeof(A); //1
解析:类的实例化就是为每个实例在内存中分配一块地址;每个类在内存中都有唯一的标识,因此空类被实例化时,编译器会隐含地为其添加一个字节,以作区分。
2.虚函数类
class A
{
virtual void Fun();
};
sizeof(A); //4
解析:当一个类中包含虚函数时,会有一个指向其虚函数表的指针vptr,系统为类指针分配大小为4个字节(即使有多个虚函数)。
3.普通数据成员
class A
{
int a;
char b;
};
sizeof(A); //8
解析:普通数据成员,按照其数据类型分配大小,由于字节对齐,所以a+b=8字节。
4.静态数据成员
class A
{
int a;
static int b;
};
sizeof(A); //4
解析:
1、静态数据成员存放的是全局数据段,即使它是类的一个成员,但不影响类的大小;不管类产生多少实例或者派生多少子类,静态成员数据在类中永远只有一个实体存在。
2、类的非静态数据成员只有被实例化时,才存在。但类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在。类的静态数据成员可以说是一种特殊的全局变量。
5.普通成员函数
class A
{
void Fun();
};
sizeof(A); //1
解析:类的大小与它的构造函数、析构函数以及其他成员函数无关,只与它的数据成员相关。
6.普通继承
class A
{
int a;
};
class B:public A
{
int b;
};
sizeof(B); //8
解析:普通类的继承,类的大小为本身数据成员大小+基类数据成员大小。
7.虚函数继承
virtual class A
{
int a;
};
class B:virtual public A
{
int b;
};
sizeof(B); //12
四、类的访问权限
- C中struct 的所有行为和属性都是 public 的(默认),此举也是为了 C++兼容 C 语言,因为 C 语言中没有权限的概念。
- C++中的 class 是由C中struct演变而来。C++不同与C,它对外屏蔽了部分数据,只提供对外的接口。但C++中class可以指定行为和属性的访问方式,若不指定默认为 private。
类内成员属性 | 类内部访问权限 | 对象访问权限 |
public | 可访问 | 可访问 |
private | 可访问 | 不可访问 |
五、类的存储方式
用类去定义对象时,系统会为每一个对象分配存储空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。
- 1、存在问题:如果用同一个类定义了 10 个对象,那么就需要分别为 10 个对象的数据和函数代码分配存储单元?
- 问题解析:如果按上述方法,那样会大大的浪费内存空间,C++编译器在分配时只对各个对象的数据部分单独分配内存,对象的函数体分配的是同一段公用的内存。
- 2、存在问题:既然函数代码段调用的是同一个,各对象的在调用时怎么知道当前是哪个对象在调用本函数?
- 问题解析:C++在对象在调用公用函数时,将对象的指针(This)作为参数隐士的传递给了公用函数用来识别当前调用者对象。
-------------------------------------- 适合自己的才是最好的!-----------------------------------------