Day38、引用、引用与指针、类型转换(隐式、显式)、类和对象、构造函数、对象的创建和销毁、电子时钟

(接昨天引用)

4、引用型函数返回值

1)可以将函数的返回类型声明为引用,避免函数返回值所带来的内存开销,如果一个函数返回类型被声明为引用,那么该函数的返回值就是一个左值

例:

2)为了避免在函数外部修改引用的目标变量,可以为该引用附加常属性。

  1#include<iostream>

  2using namespace std;

  3struct A{

 4     int data;

 5     const int& foo(void){

 6         return data;

 7     }

  8 }

  9int main(void){

 10     Aa={0};//data=0;

 11  //  a.foo()=100;  将100赋值给data,因为加了const,所以执行不了

 12    cout<<a.data<<endl;//100

 13    return 0;

 14 }

3)回局部变量的引用,危险!不要从函数中返回局部变量的引用,因为所引用的内存会在函数返回以后被释放

  1#include<iostream>

  2using namespace std;

  3struct A{

 4     int data;

 5     int& foo(void){

 6         return data;

 7     }

  8//返回局部变量的引用,危险!引用这种已经被释放的变量,内存地址可能被其它变量>    所使用,导致结果异常

 9     int& bar(void){

 10        int data=1000;

 11        return data;

 12     }

 13     int& hum(void){

 14        int data=2000;

 15        return data;

 16     }

 17};

 18int main(void){

 19     Aa={0};//data=0;

 20    // a.foo()=100;

 21    // cout<<a.data<<endl;//100

 22    int& r=a.bar();

23    cout<<r<<endl;

 24    a.hum();//不做任何处理

 25    cout<<r<<endl;

 26    return 0;

 27 }

4)可以在函数中返回成员变量,静态变量,全局变量的引用

5引用与指针(笔试题)

1) 引用本质就是指针,但是建议使用引用而不是指针

Double d=1.1234;

Double&rd=d;  //rd

Double* constpd=&d ;  //*pd

2) 指针可以不初始化,其指向目标可以修改(除指针常量),而引用定义时必须初始化,且一旦初始化引用目标后不能修改。

int a,b;

int *p;

p=&a; //ok

p=*b; //ok

 

int& r; //error

int* r=a;  //r引用变量a

r=a; // 把b的值复制给a

3) 可以定义指针的指针(二级指针),但不能定义引用的指针

int a=10;

int *p=&a;

int**p=&p;//ok

 

int& r=a;

int&*pr=&r;//error

4) 可以定义指针的引用,但是不能定义引用的引用

int a=10;

int*p=&a;

int*&rp=p;//OK

 

int& r=a;

int&&rr=r ; //error

5) 可以定义指针数组,但是不能定义引用数组

inta=10,b=20,c=30;

int*arr[3]={&a,&b,&c};//ok   指针数组

int&rarr[3]={a,b,c};//error

6) 可以定义数组引用

intarr[3]={1,2,3};

int(&r)[3]=arr; //r就是数组的别名

arr[0] === r[0];

7) 和函数指针一样,可以定义函数的引用,其语法基本一致

void func(inta.inb){

        cout<<a<<b<<endl;

}

Int main(void){

        void(* func)(int, int)=func;

pfunc(10,20); // 10  20

        void(&rfunc)(int ,int )=func;

        rfunc(10,20); //10  20

}

一、            C++类型转换

1、 隐式类型转换

char c=’A’;

int n=c;// 隐式类型转换

 

void foo(int i){….}

int foo(void){

        char c=’A’;

        return c;// 隐式类型转换

}

int main(void){

        char c=’A’;

foo(c); //u;隐式类型转换  

}

2、 显式类型转换

2.1:C语言强制类型转换

int n=(int)c ; // C的强制转换

int n=int (c);// C++ 风格的强制转换

2.2:C语言兼容C的强制转换,同时增加了四中操作符形式的显示转换

1)静态类型转换

目标类型变量=static_cast<目标类型>(源类型变量)

-à主要用于将void *转换为其它类型的指针

-à可以使用所有隐式类型转换的地方

int a;

void *pv=&a;// int * à void*  OK

int *pi=pv; //void* à int* ,error, 无法隐式转换

int*pi=static_cast<int *>(pv);   OK

例:

  1 #include<iostream>

  2 using namespace std;

  3 int main(void){

  4    int* pi=NULL;

  5    void* pv=pi;//OK

  6    pi=static_cast<int*>(pv);//静态类型转换

  7    char c='A';

  8 // pi=static_cast<int*>(c); error从char到int*,即从变量值到地址是不合理转换

  9    return 0;

 10 }

2)动态类型转换

目标类型变量=dynamic_cast<目标类型>(源类型变量)

3)常类型转换

目标类型变量=const_cast<目标类型>(源类型变量)

用于去除一个指针或引用的常属性

int a=100;

constint*pa=&a;

*pa=200 ;//error

int*pa2=const_cast_cast<int *>(pa)

*pa2=200;  // OK

例:

  1 #include<iostream>

  2 using namespace std;

  3 int main(void){

  4 /*

  5 *volatile修饰变量意味着易变的,告诉编译器每次使用该变量时,都从内存中读取,

    而不是取寄存器中的副本,防止编译器优化引发错误的结果

  9  * */

 10    const volatile int ci=100;

 11    // int* pci=&ci;  error,constint -->int

 12    int*pci=const_cast<int*>(&ci);

 13    *pci=200;

 14    cout<<"ci="<<ci<<endl;

 15    cout<<"*pci="<<*pci<<endl;

 16    cout<<"&ci"<<(void *)&ci<<endl;//地址的默认类型void *

 17    cout<<"pci"<<pci<<endl;

 18 }  

4)重解释类型转换

目标类型变量=reinterpret_cast<目标类型>(源类型变量)

à任意类型指针或引用之间的转换

à在指针和整型数之间的转换

  1 #include<iostream>

  2 using namespace std;

  3 int main(void){

  4    int num=0x12346578;

  5    void* pnum=reinterpret_cast<void*>(num);

  6    cout<<pnum<<endl;

  7    char text[]="0001\000123456\000888888";

  8    struct T{

  9        char type[5];

 10        char acc[7];

 11        char passwd[7];

 12    };

 13    T* pt=reinterpret_cast<T*>(text);

 14    cout<<pt->type<<endl;

 15    cout<<pt->acc<<endl;

 16    cout<<pt->passwd<<endl;

 17    return 0;

 18 }

0x12346578

0001

123456

888888

 

小结:C++之父给程序员建议

1、 慎用宏,代之以const \ enum \ inline

#define PAI3.14  è  const double PAI =3.14

#defineSTATE_SLEEP  0

#defineSTATE_RUN   1

#defineSTATE_STOP   2

==è enum STATE{ SLEEP,RUN,STOP }

#definemax(a,b)  ((a)>(b)?(a):(b))  预处理阶段

è  inline int max(int a,int b){

return a>b?a:b;

}

2、 变量随用随声明同时初始化

3、 尽量用new/delete替换malloc/free

4、 少用void*、指针计算、联合体、强制转换

5、  尽量用string表示字符串,少用C风格char*

二、            类和对象

1、 对象

万物皆对象,任何一种事物都可以看做是对象

2、 面向对象

2.1 如果描述表达对象

通过对象的属性(名词、数量词、形容词)和行为(动词)来表达对象

冰箱:

à属性:品牌、容量、颜色、功耗

à行为:装东西、冷冻

2.2 面向对象的程序设计

对自然世界的观察引入到编程实践中的一种方法

数据抽象:在描述对象时把细节东西剥离出去,只考虑一般性的、有规律性的、统一的东西

3、 类

类是将多个对象的共性提取出来定义的一种新的数据类型,是对对象的属性和行为抽象地描述,对象就是类的实例化。

学生对象:

属性:姓名、年龄、学号

行为:吃饭、睡觉、学习

现实世界                 类                虚拟世界

具体的对象--à抽象---à属性/行为-----实例化--à具体的对象

三、            类的定义和实例化

1、 类的一般形式

class / struct 类名:继承方式  基类…..{

       访问控制限定符:

       类名(形参表):初始化表{…} //构造函数

       ~ 类名(void){….} // 析构函数

       返回类型函数名(形参表)[const ]{ } //成员函数

       数据类型  变量名;//成员变量

};

2、 访问控制限定符

public:公有成员,谁都可以访问的成员

private:私有成员,只有类自己内部可以访问的成员

protected:保护成员

:class 缺省访问控制属性为私有,struct 缺省为公有

class A{

public:

xxxxx ; //公有

private:

xxxxx; // 私有

};

       int main{

       xxxx

}

例:

#include<iostream>

using namespacestd;

/*struct*/classstudent{

private:

    //成员都是似有的

public:

    //以下成员都是公有的

    //行为

    void eat(const string& food){

        cout<<"我吃"<<food<<endl;

    }

    void learn(const string& course){

        cout<<"我学"<<course<<endl;

    }

    void who(void){

        cout<<"我叫"<<m_name<<",今年"<<

            m_age<<"岁了,学号"<<m_no<<endl;

    }

    void setName(const string& name){

        if(name=="二")

            cout<<"你才"<<name<<endl;

        else

            m_name=name;

    }

    void setAge(int age){

        if(age<0)

            cout<<"无效年龄"<<endl;

        else

            m_age=age;

    }

    void setNo(int no){

        if(no<1000){

            cout<<"无效学号"<<endl;

        }

        else

            m_no=no;

    }

    //封装特性

    /*私有成员不能在类的外部直接访问,但是可以提供公有的接口函数间接访问,这样可以对非法数据加以限定,控制合理性*/

private:

    // 属性

    string m_name;

    int m_age;

    int m_no;

};

int main(void){

    /*原来叫定义一个结构体变量,现在在面向对象变成中叫做创建对象,或对象实例化*/

    student s;

    s.setName("张飞");

    //s.m_name="张飞";error,因为私有成员不能在类的外部直接访问

    s.setName("二");

    s.setAge(26);

    s.setAge(-1);

    s.setNo(10001);

    s.who();

    s.eat("兰州拉面");

    s.learn("C++编程");

    return 0;

}

你才二

无效年龄

我叫张飞,今年26岁了,学号10001

我吃兰州拉面

我学C++编程

 

练习:实现一个Dog类,实例化一个Dog对象

Dog属性:犬种、犬龄、体重、毛色

Dog行为:进食、睡觉、玩耍

  1 #include<iostream>

  2 using namespace std;

  3 class dog{

  4 private:

  5    string d_type;

  6    int d_age;

  7    int d_weight;

  8    string d_color;

  9 public:  

 10    void eat(const string& food){

 11        cout<<"我吃"<<food<<endl;

 12     }

 13    void sleep(const string& slep){

 14        cout<<"我爱"<<slep<<endl;

 15     }

 16    void play(const string& enjoy){

 17        cout<<"我爱"<<enjoy<<endl;

 18     }

 19    void settype(const string& type){

 20        d_type=type;

 21     }

 22    void setage(const int& age){

 23        d_age=age;

24     }

 25    void setweight(const int& weight){

 26        d_weight=weight;

 27     }

 28    void setcolor(const string& color){

 29        d_color=color;

 30     }

 31    void who(void){

 32        cout<<"我是"<<d_type<<"品种,"<<"犬龄"<<d_age<<endl;

 33        cout<<"体重"<<d_weight<<",毛色是"<<d_color<<endl;

 34     }

 35 };

 36 int main(void){

 37    dog wang;

 38    wang.settype("goldenFair");

 39    wang.setage(5);

 40    wang.setweight(30);

 41    wang.setcolor("golden");

 42    wang.who();

 43    wang.eat("meat");

 44    wang.sleep("sleep");

 45    wang.play("play");

 46    return 0;

 47 }

我是goldenFair,犬龄5

体重30,毛色是golden

我爱吃meat

我爱sleep

我爱play

3、 构造函数

class 类名{

        类名(){

               构造函数体;

}

};

1) 函数名与类名相同,而且没有返回类型

2) 构造函数在对象被创建时自动被调用

3) 构造函数主要负责初始化成员变量,以及分配必要的资源

4) 构造函数在每个对象的生命周期一定会被调用,而且只会被调用一次

例:

#include<iostream>

using namespacestd;

/*struct*/classstudent{

private:

    //成员都是似有的

public:

    //以下成员都是公有的

    student(const string& name,int age,intno){

        cout<<"构造函数"<<endl;

        m_name=name;

        m_age=age;

        m_no=no;

    }

    //行为

    void eat(const string& food){

        cout<<"我吃"<<food<<endl;

    }

    void learn(const string& course){

        cout<<"我学"<<course<<endl;

    }

    void who(void){

        cout<<"我叫"<<m_name<<",今年"<<

            m_age<<"岁了,学号"<<m_no<<endl;

    }

    void setName(const string& name){

        if(name=="二")

            cout<<"你才"<<name<<endl;

        else

            m_name=name;

    }

    void setAge(int age){

        if(age<0)

            cout<<"无效年龄"<<endl;

        else

            m_age=age;

    }

    void setNo(int no){

        if(no<1000){

            cout<<"无效学号"<<endl;

        }

        else

            m_no=no;

    }

    //封装特性

    /*私有成员不能在类的外部直接访问,但是可以提供公有的接口函数间接访问,这样可以对非法数据加以限定,控制合理性*/

private:

    // 属性

    string m_name;

    int m_age;

    int m_no;

};

int main(void){

    /*原来叫定义一个结构体变量,现在在面向对象变成中叫做创建对象,或对象实例化*/

#if 0   

    student s;

    s.setName("张飞");

    //s.m_name="张飞";error,因为私有成员不能在类的外部直接访问

    s.setName("二");

    s.setAge(26);

    s.setAge(-1);

    s.setNo(10001);

    s.who();

    s.eat("兰州拉面");

    s.learn("C++编程");

#endif

    //以有参方法创建一个对象,参数传递给构造函数

    student s("张飞",25,10011);//构造对象

    //和上面写法完全等价

    //student s=student("张飞",25,10011);

    s.who();

    s.eat("兰州拉面");

    s.learn("C++编程");

    return 0;

}

张飞

4、 类的声明和定义可以放在不同的文件中

类的声明放在stu.h中

对于大型程序开发:

vi stu.h

  1 #ifndef __STU_H_

  2 #define __STU_H_

  3 #include<iostream>

  4 using namespace std;

  5 class student{

  6 public:

  7    student(const string& name);

  8    void who(void);

  9 private:

 10    string m_name;

 11 };

 12 #endif//__STU_H_

 

类的实现放在stu.cpp中

vi stu.cpp

  1 #include"4stu.h"

  2 //通过作用域限定说明属于哪个类

  3 student::student(const string& name){

  4    m_name=name;

  5 }

  6 void student::who(void){

  7    cout<<m_name<<endl;

  8 }

 

使用该类的代码通常还会在其它文件中main.cpp

vi main.cpp

  1 #include"4stu.h"

  2 int main(void){

  3    student s("张飞");  //在栈区创建一个对象

  4    s.who();

  5     return 0;

  6 }

tarena@tarena-virtual-machine:~/day38$g++ 4main.cpp 4stu.cpp

tarena@tarena-virtual-machine:~/day38$./a.out

5、 对象的创建和销毁

1) 在栈区创建单个对象

类名:对象(构造实参表)

类名:对象=类名(构造实参表);//和上面等价

2) 在栈区创建对象数组

类名对象数组[元素个数]={类名(构造实参表)…..}

3) 在堆区创建单个对象

创建:类名* 对象指针= new 类名(构造实参表);

销毁:delete 对象指针;

4) 在堆区创建/销毁对象数组

创建:类名* 对象指针=new 类名[元素个数]{类名(构造实参表),….}

注:这种初始化的语法仅在C++11中支持

销毁:delete[ ]对象指针

例:

#include"4stu.h"

int main(void){

    //在栈区创造单个对象

    student s("张飞");

    //等价于student s=student("张飞")

    s.who();

    //在栈区创建多个对象

    student sa[3]={student("悟空"),

                    student("关羽"),

                    student("赵云")};

    sa[0].who();

    sa[1].who();

    sa[2].who();

    //在堆区创建单个对象

    student* ps=new student("曹操");

    ps->who();

    delete ps;

    ps=NULL;

    //在堆区创建对象数组,初始化表C++11支持

    ps=new student[3]{

        student("宝玉"),student("宝强"),student("黛玉")};

    ps[0].who();

    ps[1].who();

    ps[2].who();

    delete[]ps;

    ps=NULL;

    return 0;

}

tarena@tarena-virtual-machine:~/day38$g++ 4main.cpp 4stu.cpp

4main.cpp: 在函数‘int main()’中:

4main.cpp:20:21:警告: 扩展初始值设定列表只在 -std=c++0x 或 -std=gnu++0x 下可用 [默认启用]

tarena@tarena-virtual-machine:~/day38$./a.out

张飞

悟空

关羽

赵云

曹操

宝玉

宝强

黛玉

 

练习:实现一个电子时钟类,要求用其构造函数参数接收当前的系统时间,以秒为单位运行。

class clock{

public:

       clock(time_tt){

tm*local=localtime(&t);

时=local->tm_hour

分=local->tm_min

秒=local->sec

};

Void run(void){

       While(1){

       计时+1s;

       Sleep(1);

       }

}

private

       时、分、秒

};

int main{

       clock(time(NULL));

}

//获取日历时间,1970,1,1,00:00:00—现在经历秒数

time_t time(NULL);

//将日历时间转换为本地时间,将结果保存tm结构体

Struct tm* local=localtime(&t)

 

#include<iostream>

#include<cstdio>

using namespace std;

class c_Clock{

public:

   c_Clock(time_t t){

       tm* local=localtime(&t);

       m_hour=local->tm_hour;

       m_min=local->tm_min;

       m_sec=local->tm_sec;

    }

   void run(void){

       while(1){

           show();

           tick();

       }

    }

private:

   void show(void){

       printf("\r%02d:%02d:%02d",m_hour,m_min,m_sec);

       fflush(stdout);//目的是清空缓冲,强制结果马上显示在屏幕上

       //调用printf时,输出结果一般会被标准库缓存起来,不一定能及时写在屏幕上,但是fflush(stdout)能强制把缓存内容输出

    }

   void tick(void){

       sleep(1);

       if(++m_sec==60){

           m_sec=0;

           if(++m_min==60){

                m_min=0;

                if(++m_hour==24){

                    m_hour=0;

               }

           }

 

       }

    }

private:

   int m_hour;

   int m_min;

   int m_sec;

};

int main(void){

   c_Clock clock(time(NULL));

   clock.run();

   return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值