C++类与对象的定义
文章目录
C++内存中存储数据的两大属性
第一个属性:存储时间,即数据的生命周期
存储时间与内存中数据五大存储区有关
-
栈区:先进后出,访问作用域结束,内存即被释放
用于存储局部变量
p1 p2 ——>p3 //栈顶指针,从上往下走,栈顶指针锁定最新进入的元素
-
堆区:先进先出,有程序员使用delete指定释放
-->p1 p2 p3 //从下往上走,堆顶指针锁定第一个进入的元素
-
bbs区:为进行初始化的全局、静态数据变量的数据,不会分配内存,只会记录数据所需要的空间大小
-
data区:表示已经初始化的全局变量和静态变量,保证下次调用时,保持原来的值
第二个属性:访问作用域
关键字:
-
const //无法修改数据
const总是针对它左边的东西起作用, 唯一例外的是,如果const是一个最左边的标识符,那么const将针对它右边的东西起作用,因些 const int i; 与 int const i; 意思是相同的
参考博文:http://www.cnblogs.com/JCSU/articles/1045801.html
//注意指针常量和常量指针的区别 const char * const months="my pointer"; //表示一个指向字符常量的指针常量 //字符常量表示,只能接受定义时初始化修改值 //指针常量表示,无法修改指针的指向 int const *p;
-
extern //引用其他文件变量声明,只要不定义就不分配内存,因为只是对其他变量的一个引用!!因为是引用,所以修改是有效的!!
//f1.cpp int a=1; int b=3; //f2.cpp extern int a; extern int b;
-
int等基本数据声明 //在main前声明,本文件,或者其他文件均可以使用!
-
static 静态声明 //将其放置在数据段中,从而保证共享给所有具有访问此作用域的方法访问,即保证下次调用的时候还是原来的值! 整个程序执行期间,都存在,寿命贼长!!!
作用域:
-
函数作用域
-
块作用域
-
命名空间作用域
namespace Jack{ double pail; int pal; void max(); .... } namespace Bob{ double pail; int pal; void max(); .... } Jack::pail; Jack::max(); Bob::pail; Bob::max();
-
局部作用域
-
全局作用域
-
文件作用域
-
类作用域
//类作用域表示不能从外部直接访问类成员,必须通过类名访问静态成员,通过创建实例对象访问所有公有成员 //使用直接成员运算符. 使用间接成员云算法->(指针的方式) 使用作用域解析符::(类名的方式) //类作用域常量声明 class A{ private: //失败的方式 const int months=12;//直接放在栈区!!!,创建对象前并不会给值分配空间 //第一种方式 enum {Months=12};//所有对象不包含枚举类型,没有变量默认大小为1,含有int变量则大小为4,由于enum类型相当于一个常量,放在data,bbs区域,可以通过类名访问,为所有对象所共享 //第二种方式 static const int months=12;//不会存储于对象中,而是存于静态data区,可以通过类名访问,为所有对象所共享 }
-
语句作用域 //指for循环中的(int i=0; i<10;i++) 的语句作用域
变量的初始化:
-
静态和全局变量初始化:零初始化,简单的常量初始化 //如:int a; int a=1+1;
-
局部的int的初始化,并不会初始化为0,而是产生一个大大的随机值
#include <iostream> using namespace std; int c; static int d; int main(){ static int a; int b; b=2; cout<<a<<'\n'<<sizeof(a)<<'\n'; { const int mydata=3; static int data=3; cout<<data<<'\n'; } const int *p; cout<<sizeof(*p); } //c d a均输出为0 b输出一个随机值
-
动态初始化:需要调用其他函数的动态初始化 //如:int enough=sizeof(long)+1
注意内存泄漏:
//使用动态分配内存
float *p=new float[20];
//一旦作用域结束,p将被立即释放,而动态分配的内存并没有释放,就会造成内存泄漏的问题,必须使用delete(p)主动释放
过程性编程和面向对象编程的思考方式
背景
垒球队的新成员被要求记录球队的统计数据!!
//过程性程序员:
/*
实现的功能:我要输入每位选手的名字,击中次数,击球次数,并自动计算出命中率
过程描述:我需要使用main函数调用一个输入数据函数,一个计算数据函数,一个显示数据函数,那么,如果获得下一场的数据又该如何?又需要调用一个更新统计数据函数....
之后我开始考虑如何表示这些数据,可以使用一个字符串数组来存储选手的姓名,用另外的两个个整型数据存储选手的击球次数及击中次数,使用一个float型的数组用来存储命中率;
考虑到这种数据存储很不灵活,使用一个struct组成的数组来表示整个球队!!
*/
//OOP程序员:
/*
首先,我先不考虑过程,我只在乎定义一个类---选手,这个选手有各种属性,姓名,击球次数等基本信息,并设计好存储方式;
其次,我需要能够处理这些信息的方法,如计算命中率,初始化,更新,显示信息的方法,最后我只要使用这个选手类创建一个个实例对象数组,即代表整支队伍!!
*/
C++类定义,在内存中的具体分配
//如下,类C在64位系统占有多少内存,使用sizeof(C)
class C{
public:
char a;
static char b;
void *p;
static int *c;
virtual void func1();
virtual void func1();
}//sizeof(C)=24
//1.空类占有唯一表示一个字节的地址
//2.虚函数拥有一个指针8字节
//3.静态数据和成员函数不计入栈空间,函数存入代码区,静态存入data区,无论继承了多少个子类,对象中始终只有一个虚函数表指针
//4.成员变量占有内存,但必须注意跟随系统位数的对齐原则。类的大小往往被调整到系统的整数倍,并采取就近的法则,以上变量a虽然只占有一个字节,但是一个p和一个虚拟指针占有8个字节,所以a自动补齐7个字节,结果输出为24个字节
类的构造函数和析构函数
构造函数
//为了使类像基本类型数据一样操作,可以创建对象的同时就可直接初始化
class Stock{
private:
int m_a;
std::string m_name;
public:
Stock(const string name,int a){
m_a=a;
m_name=name;
}//自定义的初始化构造函数
Stock(){
m_name ="no name";
a=0;
}//自定义默认的构造函数
};
//开始创建类的实例对象
Stock s1; //相当于一种数据类型,占有16个字节
Stock s2("my name",29); //同样也占有16个字节
析构函数
//使用构造函数创建对象,程序开始跟踪对象,当访问作用域结束并且寿命终结时,开始自动销毁,同时触发析构函数, 特别针对于使用new创建分配内存时,必须定义一个delete的析构函数,否则容易造成内存泄漏!!
什么时候开始调用析构函数?
- 如果创建的是静态寿命的对象,将在程序执行结束的时候调用析构函数
- 如果创建的是自动存储的局部对象,将在访问作用域结束时就调用析构函数,即存于栈区
- 如果创建的是new动态分配内存对象,将在使用delete是调用析构函数,即存于堆区
注意delete和delete[ ] 的区别?
//1.区别:
/*
delete 释放指向的全部数组内存,但是只会调用第一个析构函数,释放一个用户自己new的内存,剩下的用户内存并没有被释放,从而造成内存泄漏
delete[] 释放指向的全部数组内存,并调用所有的析构函数,释放所有用户new的内存,更加安全
*/
class A
{
private:
char *m_cBuffer;
int m_nLen;
public:
A(){ m_cBuffer = new char[m_nLen]; }
~A() { delete [] m_cBuffer; }
};
A *a = new A[10];
delete a; //释放了a全部数据,但是只调用一个析构函数,构造函数a[1~9]new的m_cBuffe数据都没有被释放,从而造成内存泄漏,非常容易导致程序crash!!
//2.使用方法:
/*
只要构造函数没有自己new出数据,析构函数可以无需定义,delete和delete[]均可以达到目的
一旦构造函数自己new出数据,必须定义一个delete析构函数,而且必须使用delete[],否则容易内存泄漏!!!
*/
//3.事例:
#include <iostream>
using namespace std;
/class Babe
class Babe
{
public:
Babe()
{
cout << "Create a Babe to talk with me" << endl;
}
~Babe()
{
cout << "Babe don\'t go away,listen to me" << endl;
}
};
//main function
int main()
{
Babe* pbabe = new Babe[3];
delete pbabe;
pbabe = new Babe[3];
delete []pbabe;
return 0;
}
//结果是:
Create a babe to talk with me
Create a babe to talk with me
Create a babe to talk with me
Babe don\'t go away,listen to me
Create a babe to talk with me
Create a babe to talk with me
Create a babe to talk with me
Babe don\'t go away,listen to me
Babe don\'t go away,listen to me
Babe don\'t go away,listen to me
参考博文:https://www.cnblogs.com/wangjian8888/p/7905176.html
类对象的this指针
//作用
top= stock1.topval(stock2);
const Stock & topval(const Stock &s) const{
if(s.m_val>m_val) return s;
else return *this;//表示返回当前对象,而非一个地址
}//常量函数,表示不能修改通过this指针修改对象值
//默认所有成员方法都传入一个Stock *this指针参数,表示指向当前对象的地址
类中的常量函数,常量引用参数,常量引用返回值
class Person{
public:
string m_name;
const string & set_name() const;//常量函数表示,不能通过this指针修改对象的值
sting & evil_name() const;//常量引用返回值表示,不能修改返回值
};
void my_code(const Person &p){//常量引用参数表示,不能修改参数
p.m_name="yang";//出现编译错误
p.evil_name()="my name";//又再次试图修改返回值
}
参考博文:http://www.cnblogs.com/JCSU/articles/1045801.html
对象数组
Stock stock[10]={
Stock("yang",12,32),
Stock("xu",143,342),
Stock("zhao",142,3322)
};//表示创建10个Stock对象的数组,并初始化前三个对象!
定义实现一个栈数据类型
/*
分析:可以使用一个数组用来存储数据,使用数组下标top指向最新压入的数据
需要定义isempty(),isfull(),pop(),push(),四个方法
*/
//stack.h
#ifndef STACK_H
#define STACK_H
typedef unsigned long Item;
class Stack{
private:
enum {MAX =10};
Item items[MAX];
int top;
public:
Stack(){top=0;};
bool isempty() const{return top==0};
bool isfull() const{return top==MAX-1};
bool pop(Item &item){
if(top>0){
item=items[--top];
return true;
}else{
return false;
}
};
bool push(const Item &item){
if(top<MAX-1){
items[top++]=item;
return true;
}else{
return false;
}
};
}
#endif
//注:为了方便,定义与实现我写在一块了!