C++学习笔记(三)

十.C++的引用(Reference)

3.引用型函数参数

①可以将引用用于函数的参数,这时形参将是实参的别名,可以通过形参直接修改实参变量,同时还可以避免参数值传递的过程,减小函数调用开销,提高代码执行效率。
②引用型参数有可能意外的修改实参,如果不希望修改实参本身,可以将参数定义为常引用,提高传参效率的同时还可以接收常量型的实参。
eg.1

#include <iostream>
using namespace std;

/*这是C的做法*/
/*void swap1(int* x,int* y){
    *x = *x ^ *y;
    *y = *x ^ *y;
    *x = *x ^ *y;
}*/
/*这是C++的做法*/
// 相当于是给a、b起了个别名int& x=a,int& y=b
void swap2(int& x,int& y){
    x = x ^ y;
    y = x ^ y;
    x = x ^ y;
}
void swap3(const char*& x,const char*& y ){
    const char* tmp = x;
    x = y;
    y = tmp;
}
int main(void){
    int a = 3;
    int b = 5;
    cout << "a=" << a << ",b=" << b << endl; 
    //swap1(&a,&b);
    swap2(a,b);
    cout << "a=" << a << ",b=" << b << endl; 
    //练习:使用引用参数,实现两个const char*字符串的交换
    const char* s1 = "wangjl";
    const char* s2 = "yangjian";
    swap3(s1,s2);
    cout << "s1=" << s1 << endl;//yangjian
    cout << "s2=" << s2 << endl;//wangjl

    return 0;
}

4.引用型函数返回值

①可以将函数的返回类型声明为引用,这时函数的返回结果就是return后面数据的别名,可以避免函数返回值所带来的的内存开销,提高代码执行效率.
②如果函数返回结果是一个普通(左值)引用,那么该函数调用表达式结果就也是左值.
(注:不要从函数中返回局部变量的引用,因为所引用的内存会在函数返回以后被释放,使用非常危险!但是可以从函数中返回成员变量、全局变量、静态变量的引用。 )

#include <iostream>
using namespace std;
struct A{
    int data;
    int& func(void){
        return data;
    }
    int& func2(void){
        int num = 100;
        return num;//危险!
    }
};
int main(void){
    A a = {100};
    cout << a.data << endl;//100
    //a.data = 200
    a.func() = 200;//ok
    cout << a.data << endl;//200

    return 0;
}

5.引用和指针

①从C语言角度看引用的实现,其本质就是指针,但是C++开发中推荐使用引用而不是指针.

int i = 100;
int* const pi = &i;
int& ri = i;
*pi <==> ri

②指针可以不做初始化,其指向的目标可以修改(指针常量除外);而引用必须初始化,一旦初始化其绑定目标的不能再修改。

int a,b;
int* p;//ok
p = &a;//p指向a
p = &b;//p指向b
---------------
int& r;//error
int& r = a;//r引用a,r就是a别名
r = b;//ok,不是修改引用目标,仅是赋值操作

③可以定义指针的指针(二级指针),但是不能定义引用的指针(了解)

int a;
int* p = &a;
int** pp = &p;//ok,二级指针
-----------------------
int& r = a;
int&* pr = &r;//error,引用的指针	
int* pr = &r;//ok,但仅是普通指针

④可以定义指针的引用(指针变量的别名),但是不能定义引用的引用(了解)

int a;
int* p = &a;
int* & rp = p;//ok,指针的引用
-----------------------
int& r = a;
int& & rr = r;//error,引用的引用 
int& rr = r;//ok,但仅是普通的引用

⑤可以定义指针数组,不能定义引用数组,但可以定义数组引用(数组的别名)、了解

int a=10,b=20,c=30;
int* parr[3]={&a,&b,&c};//ok,指针数组
int& rarr[3]={a,b,c};//error,引用数组
-------------------------
int arr[3] = {a,b,c};
int (&rarr)[3] = arr;//数组引用

⑥和函数指针也可以,可以定义函数引用(函数的别名),其语法特性和函数指针一致(了解)

void func(int a,int b){}
int main(void){
	void (*pfunc)(int,int) = func;//ok,函数指针
	void (&rfunc)(int,int) = func;//ok,函数引用
	pfunc(...);
	rfunc(...);
}

十一.类型转换

1.隐式类型转换

char c = 'a';
int i = c;//隐式
----------------
void func(int i){}
func(c);//隐式
---------------
int func(void){
  		char c = 'a';
  		return c;//隐式
  }

2.显式类型转换

2.1 C++兼容C语言的强制转换

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

2.2 C++增加了四种操作符形式的类型转换

① 静态类型转换:static_cast 语法:
目标变量 = static_cast<目标类型>(源类型变量);
适用场景:主要用于将void*转换为其它类型的指针

#include <iostream>
using namespace std;

int main(void){
    int* pi = NULL;
    //char c = (int)pi;//C风格强制转换
    char c = int(pi);//C++风格强制转换
    
    //静态类型转换
    //char c2 = static_cast<int>(pi);//不合理 
    void* pv = pi;
    pi = static_cast<int*>(pv);//合理


    return 0;
}

②动态类型转换:dynamic_cast
语法: 目标变量 = dynamic_cast<目标类型>(源类型变量);
适用场景:只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,如果是非法的对于指针返回NULL,对于引用抛异常。

③(去)常类型转转:const_cast
语法: 目标变量 =const_cast<目标类型>(源类型变量);
适用场景: 主要用于去除指针或引用的const属性

#include <iostream>
using namespace std;
int main(void){
    /* volatile修饰变量表示易变的,告诉编译器每次
     * 使用该变量都要内存中重新读取,而不是取寄存
     * 器中副本,防止编译器优化引发的错误结果.
     * */
    volatile const int ci = 100;
    int* pci = const_cast<int*>(&ci);
    *pci = 200;
    cout << "ci=" << ci << endl;//200
    cout << "*pci=" << *pci << endl;//200
    cout << "&ci=" << (void*)&ci << endl;
    cout << "pci=" << (void*)pci << endl;

    return 0;
}

④重解释类型转换:reinterpret_cast
语法: 目标变量 = reinterpret_cast<目标类型>(源类型变量);
适用场景:
–》任意类型指针或引用之间的显式转换
–》在指针和整型数之间的显式转换

#include <iostream>
using namespace std;

int main(void){
    //"\000"->'\0'
    char buf[] = "0001\00012345678\000123456";
    struct Http{
        char type[5];
        char id[9];
        char passwd[7];
    };
    Http* ph = reinterpret_cast<Http*>(buf);
    cout << ph->type << endl;//0001
    cout << ph->id << endl;//12345678
    cout << ph->passwd << endl;//123456
    return 0;
}
eg:已知物理内存地址0x12345678,向存放数据100?
int* paddr = reinterpret_cast<int*>(0x12345678);
*paddr = 100;

小结:
1.慎用宏,可以使用const、enum、inline

  #define PAI 3.14
  		--> const double PAI = 3.14;
  #define SLEEP 0
  #define RUN   1
  #define STOP  2
  		--> enum STATE{SLEEP,RUN,STOP};
  #define Max(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*/char[]

十二.类和对象

1.什么是对象
万物皆对象,任何一种事物都可以看做是对象.
2.如何描述对象
通过对象的属性(名词、数量词、形容词)和行为(动词)来描述对象。
3.面向对象程序设计
对自然世界中对象的观察引入到编程实践中一种理念和方法,这种方法被称为"数据抽象",即描述对象时把细节东西剥离出去,只考虑一些有规律的、一般性、统一性的东西。
4 什么是类
类是多个对象的共性提取出来定义的一种新的数据类型,是对 对象属性和行为的抽象描述。
现实世界 类 虚拟世界
具体对象–抽象–》属性/行为–实例化–》具体对象

十三.类的定义和实例化

1.类的一般语法形式

struct/class 类名:继承方式 基类,...{
  访问控制限定符:
  		类名(形参表):初始化列表{...}//构造函数
  		~类名(void){...}//析构函数
  		返回类型 函数名(形参表){...}//成员函数
  		数据类型 变量名;//成员变量
  };

2.访问控制限定符

①public:公有成员,任何位置都可以使用
②private:私有成员,只有类自己的成员函数才能使用
③protected:保护成员(后面讲)
(注:struct定义类默认的访问控制属性是public,class定义类默认的访问控制属性是private)
eg.

struct/class XX{
		int a;//默认
private:
		int b;//私有成员
public:
		int c://公有成员
		int d;//公有成员
private:
		int e;//私有成员
	};

eg.

#include <iostream>
using namespace std;
//原来:结构体
//现在:类
//struct Student{//默认:public
class Student
{//默认:private
public:
    //行为:成员函数
    void eat(const string& food){
        cout << "我在吃" << food << endl;
    }
    void sleep(int hour){
        cout << "我睡了" << hour << "小时"<<endl;
    }
    void learn(const string& course){
        cout << "我在学" << course << endl;
    }
    void who(void){
        cout << "我叫" << m_name << ",今年" << 
            m_age << "岁,学号是" << m_no << endl;
    }
public:
    /* 类中私有成员在外部不能直接访问,但是可以提
     * 供类似如下的公有函数来间接访问,在该函数中
     * 可以对非法数据加以限定,控制业务逻辑合理性
     * 这种编程思想就是封装!*/
    void setName(const string& newName){
        if(newName == "二"){
            cout << "你才二" << endl;
        }
        else{
            m_name = newName;
        }
    }
    void setAge(int newAge){
        if(newAge < 0){
            cout << "非法的年龄" << endl;
        }
        else{
            m_age = newAge;
        }
    }
    void setNo(int newNo){
        if(newNo < 0){
            cout << "非法的学号" << endl;
        }
        else{
            m_no = newNo;
        }
    }
private:
    //属性:成员变量
    string m_name;
    int m_age;
    int m_no;
};
int main(void){
    //原来:定义结构体变量
    //现在:创建对象/实例化对象/构造对象
    Student s;
    /*s.m_name = "张飞";
    s.m_name = "二";
    s.m_age = 28;
    s.m_no = 10011;*/
    s.setName("二");
    s.setName("张翼德");
    s.setAge(-1);
    s.setAge(29);
    s.setNo(-1);
    s.setNo(10086);

    s.who();
    s.eat("五香牛肉干");
    s.sleep(8);
    s.learn("面向对象编程");

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

boss-dog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值