C++类与对象

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
//注:为了方便,定义与实现我写在一块了!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值