C++学习——类型转换,类和对象,类的定义和实例化

十一、类型转换

1.隐式转换

//1
char c = 'a';
int i = c;  //隐式转换

//2
int func(void)
{
    char c = 'a';
    return c;  //隐式转换
}

//3
void func(int i){}
char c = 'a';
func(c); //隐式转换

2.显示转换

1)C++兼容C中强制类型转换

	char c = 'a';
	int i = (int)c;//C风格的强转
	int i = int(c);//C++风格的强转

2)C++扩展了四种操作符形式的类型转换

①静态类型转换

语法:
    目标类型变量=static_cast<目标类型>(源类型变量)
使用场景:
    用于将void*转换为其它类型的指针。

#include <iostream>
using namespace std;

int main(void)
{
    int* pi = NULL;
    //int num = (int)pi;//C风格
    //int num = int(pi);//C++风格
    //int num = static_cast<int>(pi);//error
    
    void* pv = pi;
    pi = static_cast<int*>(pv);//合理,ok

    return 0;
}


②动态类型转换
语法:
    目标类型变量=dynamic_cast<目标类型>(源类型变量);
③常类型转换
语法:
    目标类型变量=const_cast<目标类型>(源类型变量)
使用场景:
    用于去除指针或引用的常属性

#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=" << pci << endl;
    
    return 0;
}


④重解释类型转换
语法:
    目标类型变量=reinterpret_cast<目标类型>(源类型变量)
使用场景:
    --》指针和整数之间进行显式转换
    --》在任意类型的指针或引用之间显式转换
    eg:向物理地址0x12345678存放一个数据100
        int* paddr=reinterpret_cast<int*>(0x12345678);
        *paddr = 100;

#include <iostream>
using namespace std;

int main(void)
{
    //"\000"-->'\0'-->0
    char buf[]="0001\00012345678\000123456";
    struct Net{
        char type[5];
        char id[9];
        char passwd[7];
    };
    Net* pn = reinterpret_cast<Net*>(buf);
    cout << pn->type << endl;//0001
    cout << pn->id << endl;//12345678
    cout << pn->passwd << endl;//123456

    return 0;
}

小结:来自C++社区给C程序的建议

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 void 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 类的一般语法形式

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


2 访问控制限定符
1)public:公有成员,任何位置都可以访问
2)private:私有成员,只有类内部中的成员函数才能访问
3)protected:保护成员(后面讲)
注:使用class定义的类,类中成员的默认访问控制属性是private;如果使用struct定义的类,类中成员默认的访问控制属性是public.
eg:

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

3.构造函数(constructor):主要完成对象的初始化

1)函数名和类型相同,没有返回值
2)构造函数在创建对象时自动调用,不能显示调用

#include <iostream>
using namespace std;
class Student{
public:
    Student(const string& name,int age,
        int no){
        cout << "构造函数" << endl;
        m_name = name;
        m_age = age;
        m_no = no;
    }
    void who(void){
        cout << "我叫" << m_name << ",今年"
            << m_age << "岁,学号是" << m_no
            << endl;
    }
private:
    string m_name;
    int m_age;
    int m_no;
};
int main(void)
{
    //创建对象,(...):指明构造函数需要的实参
    Student s("张飞",25,10086);
    s.who();
    //构造函数不能像普通成员函数一样显式调用
    //s.Student("张三",26,10011);
    return 0;
}

4.对象的创建和销毁

1)在栈区创建单个对象

类名 对象(构造实参表);
类型 对象 = 类名(构造实参表);

2)在栈区创建多个对象

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

3)在堆区创建/销毁单个对象

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

注:new操作符除了会分配内存还会自动表用构造函数,而malloc只会分配内存,不会调用构造函数。

销毁:
delete 对象指针

4)在堆区创建/销毁多个对象

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

参考代码:

#include <iostream>
using namespace std;
class Student{
public:
    Student(const string& name,int age,
        int no){
        cout << "构造函数" << endl;
        m_name = name;
        m_age = age;
        m_no = no;
    }
    void who(void){
        cout << "我叫" << m_name << ",今年"
            << m_age << "岁,学号是" << m_no
            << endl;
    }
private:
    string m_name;
    int m_age;
    int m_no;
};
int main(void)
{
    //创建对象,(...):指明构造函数需要的实参
    //Student s("张飞",25,10086);
    Student s = Student("张飞",25,10086);
    s.who();
    //栈中创建对象数组
    Student sarr[3] = {
        Student("貂蝉",26,10087),
        Student("小乔",22,10088),
        Student("大乔",27,10089)};
    sarr[0].who();
    sarr[1].who();
    sarr[2].who();
    //堆区创建单个对象
    Student* ps = 
        new Student("孙尚香",28,10011);
    ps->who();//(*ps).who();
    delete ps;
    ps = NULL;
    //堆区创建多个对象,C++11支持
    Student* parr = new Student[3]{
        Student("林黛玉",28,10012),
        Student("潘金莲",29,10013),
        Student("孙二娘",35,10014)};
    parr[0].who();//(*(parr+0)).who()
    parr[1].who();
    parr[2].who();
    delete[] parr;
    parr = NULL;

    return 0;
}

5.多文件编程

1)类的声明一般放在头文件中

#ifndef __STUDENT_H
#define __STUDENT_H
#include <iostream>
using namespace std;
//类的声明
class Student{
public:
    Student(const string& name,int age);
    void who(void);
private:
    string m_name;
    int m_age;
};
#endif//__STUDENT_H

2)类的定义放在源文件中

#include "Student.h"
//类的定义,需要在成员函数名字前面加"类名::"
Student::Student(const string& name,int age){
    cout << "构造函数" << endl;
    m_name = name;
    m_age = age;
}
void Student::who(){
    cout << m_name << ',' << m_age << endl;
}

十四、构造函数和初始化表

1.构造函数可以重载、也可以带有缺省参数

2.缺省构造函数(无参构造)

1)如果类中没有定义任何构造函数,编译器会提供一个缺省构造函数:

①对于基本类型的成员变量不做初始化

②对于类类型的成员变量(成员子对象),会自动调用相应的无参构造函数来初始化

	class A{
		int m_i
		string m_s;
	};
	A a;
	cout << a.m_i << endl;//未初始化的结果
	cout << a.m_s << endl;//一定是空字符串

2)如果定义了构造函数,无论是否有参数,那么编译器都不会提供缺省构造函数,构造函数的参数必须与形参完全对应,不然编译不通过

3.类型转换构造函数(单参构造函数)

class 目标类型{
	[explicit] 目标类型(源类型){...}
};

注:可以实现源类型到目标类型的隐式转换,使用explicit关键字,可以强制要求这种类型转换必须要显示完成

#include <iostream>
using namespace std;
class Integer{
public:
    Integer(void){
        cout << "Integer(void)" << endl;
        m_i = 0;
    }
    //类型转换构造函数
    /*explicit*/ Integer(int i){
        cout << "Integer(int)" << endl;
        m_i = i;
    }
    void print(void){
        cout << m_i << endl;
    }
private:
    int m_i;
};
int main(void)
{
    Integer i1;
    i1.print();//0

    //首先将20隐式转换为Integer对象
    //再使用转换之后的临时对象给i1赋值
    i1 = 20;
    i1.print();//20

    //隐式转换代码可读性差,推荐显式转换
    //i1 = (Integer)30;//C风格
    i1 = Integer(30);//C++风格
    i1.print();//30

    return 0;
}

4.拷贝构造函数

1)用已存在的对象构造同类型的副本对象,会调用该类型的拷贝构造函数;

class 类名{
    类名 (const 类名& )
    {......}
};

参考代码:
 

class A{
public:
    A(int data=0){
        m_data=data;
    }
    A(const A& that){
        cout << "拷贝构造函数" << endl;
        m_data=that.m_data;
    }
private:
    int m_data;
};
int main()
{
    A a1(100);
    A a2(a1);
    return 0;
}

2)如果类中自己没有定义拷贝构造函数,那么编译器会为该类提供一个缺省的靠背构造函数:
①对于基本类型的成员变量,按字节复制

②对于类类型的成员变量(成员子对象),将自动调用相应类的拷贝构造函数来初始化

注:一般不需要自己写拷贝构造函数,因为编译器缺省提供的已经很好用了

3)靠背构造函数调用时机

①用已经定义的对象作为同类型对象的构造实参

②以对象形式向函数传递参数

③从函数返回对象

猜你喜欢

转载自blog.csdn.net/isco22/article/details/88100928
今日推荐