布尔类型、三目运算符、引用、新型类型转换、友元

目录

布尔类型

三目运算符

C++中的引用

新型类型转换

友元


布尔类型

C++在C语言的基本类型系统之上增加了bool,C++中的bool可取的值只有true和false,理论上bool只占用一个字节        

bool类型只有true(非0)和false(0)两个值,true代表真值,编译器内部用1来表示;false代表非真值,编译器内部用0来表示

C++编译器会将非0值转换为true,0值转换为false 

布尔类型是C++中的基本数据类型,可以定义bool类型的全局变量,可以定义bool类型的常量,可以定义bool类型的指针,可以定义bool类型的数组,......

#include <stdio.h>  
  
int main(int argc, char *argv[])  
{  
    bool b = false;  
    int a = b;  
      
    printf("sizeof(b) = %d\n", sizeof(b));  // 1
    printf("b = %d, a = %d\n", b, a);       // 0 0
      
    b = 3;  
    a = b;  
      
    printf("b = %d, a = %d\n", b, a); // 1 1      
      
    b = -5;  
    a = b;  
      
    printf("b = %d, a = %d\n", b, a); // 1 1
      
    a = 10;  
    b = a;  
      
    printf("a = %d, b = %d\n", a, b); // 10 1 
      
    a = 0;  
    b = a;  
      
    printf("a = %d, b = %d\n", a, b); // 0 0 
      
    return 0;  
}  

三目运算符

C语言中的三目运算符返回的是变量值不能作为左值使用

C++中的三目运算符可直接返回变量本身既可作为右值使用,又可作为左值使用,若三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用

C++中当三目运算符的可能返回值都是变量时,返回的是变量引用,当三目运算符的可能返回值中有常量时,返回的是

int a = 1;  
int b = 2;  
  
(a < b ? a : b) = 3;      // gcc error , g++ ok
( a < b ? 1 : b ) = 4;    // gcc error , g++ error 不能作为左值
  
printf("a = %d,b = %d\n", a, b);  

C++中的引用

引用:可以看作一个已定义变量的别名,引用的语法:Type& name = var;    name为var的别名,操作name就是操作var                  

注意:普通引用在定义时必须用同类型的变量进行初始化 ,否者出错

引用的意义:引用作为变量别名而存在,因此在一些场合可以代替指针,引用相对于指针来说具有更好的可读性和实用性

void swap(int& a, int& b)   // 函数中的引用形参不需要进行初始化! 
{  
    int t = a;  
    a = b;  
    b = t;   
}  

void swap(int* a, int* b)   
{  
    int t = *a;   
    *a = *b;   
    *b = t;  
}       

const引用:在C++中可以声明const引用 ,const引用让变量拥有只读属性  :const Type& name = var

const引用的类型与初始化变量的类型 如果相同:初始化变量成为只读变量 ,如果不同:生成—个新的只读变量 

int main()
{
    /*在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理。*/ 
    /*const只是告诉编译器, const标识符不能出现在赋值符号左边*/

    int a = 4;   
    const int& b = a;  // 只读变量 
    int* p = (int*)&b;   
  
    //b = 5;  // Error 
    *p = 5; // Ok
    a = 10; // Ok
    
    const int& c = 1;   // 使用常量对const引用进行初始化时
    int* pc = (int*)&c; // C++编译器会为常量值分配空间,并将引用名作为这段空间的别名,生成一个只读变量
    //c = 10; // Error
    *p = 10; // Ok
	
    const int i = 10; // 进入符号表 
    const int& d = i; // 直接使用i的内存空间 
    cout << (&i == &d) << endl; // 1
	
    return 0;
}

思考: 引用有自己的存储空间吗? 

// 引用的思考    test.cpp
#include <stdio.h>  
  
struct TRef  
{  
    char& r;  
};  
  
int main(int argc, char *argv[])  
{   
    char c = 'c';  
    char& rc = c;  
    TRef ref = { c };  
      
    printf("sizeof(char&) = %d\n", sizeof(char&)); // 1
    printf("sizeof(rc) = %d\n", sizeof(rc));       // 1
      
    printf("sizeof(TRef) = %d\n", sizeof(TRef));   // 4  ???
    printf("sizeof(ref.r) = %d\n", sizeof(ref.r)); // 1 
  
    return 0;  
}  

引用在C++中的内部实现是一个指针常量 

void f(int& a)
{
	a = 5;
}
// 两者等价
void f(int* const a)
{
	*a = 5;
}

C++编译器在编译过程中用指针常量作为引用的内部实现,因此引用所占用的空间大小与指针相同。 

从使用的角度,引用只是一个别名,C++为了实用性而隐藏了引用的存储空间这—细节。 

#include <stdio.h>  

struct TRef
{
	char* before;
	char& ref;
	char* after;
};

int main(int argc, char* argv[])
{
	char a = 'a';
	char& b = a;
	char c = 'c';

	TRef r = { &a, b, &c };

	printf("sizeof(r) = %d\n", sizeof(r));               // 12
	printf("sizeof(r.before) = %d\n", sizeof(r.before)); // 4
	printf("sizeof(r.after) = %d\n", sizeof(r.after));   // 4


	printf("&a = %p\n", &a);
	printf("&c = %p\n", &c);
	printf("&r.before = %p\n", &r.before);
	printf("&r.after = %p\n", &r.after);

	return 0;
}

VS2015

C++只是隐藏了b是指针的事实

#include <iostream>

using namespace std;

int main()
{

   int x = 1;
   int y = 2;

   int &b = x;
   
   cout << &x << endl;
   cout << &y << endl;
   cout << hex << (&y - 1) << endl;
   cout << hex << *(&y - 1) << endl;


   return 0;
 }

gcc version 4.4.5(Ubuntu 10.10)

栈的生长方向自上向下的,通过&y-1间接获得b的地址,*(&y-1) 即指针保存的x的地址(我的VS2015局部变量间总是有8字节的间隙)

可以猜测*(&x - 2)值为bfc02c3c

函数返回引用 :如果返回局部的栈中的变量的引用就相等于返回一个失效的地址,使用这段内存是非法的

C++不支持引用数组

#include <stdio.h>  
  
int a = 1;  
  
struct SV  
{  
    int& x;  
    int& y;  
    int& z;  
};  
  
int main()  
{  
    int b = 2;  
    int* pc = new int(3);  
    SV sv = {a, b, *pc};  
      
    printf("&sv.x = %p\n", &sv.x);  
    printf("&sv.y = %p\n", &sv.y);  
    printf("&sv.z = %p\n", &sv.z);  

    
    //int& array[] = {a, b, *pc}; // &array[1] - &array[0] = ?  Expected ==> 4 
    /*error: declaration of ‘array’ as array of references*/  
      
    delete pc;  
      
    return 0;  
}  

 

对比地址发现为了兼容C语言所有特性,放弃引用数组,所以C++不支持引用数组

小结

指针是—个变量 , 值为一个内存地址,不需要初始化,可以保存不同的地址 ,通过指针可以访问对应内存地址中的值 ,指针可以被const修饰成为常量或者只读变量 

引用只是—个变量的新名字 ,对引用的操作(赎值,取地址等)都会传递到代表的变量上,const引用使其代表的变量具有只读属性 ,引用必须在定义时初始化,之后无法代表其它变量 

C++中的引用旨在大多数的情况下代替指针 

   -功能性:可以满足多数需要使用指针的场合 

   -安全性:可以避开由于指针操作不当而带来的内存错误 

   -操作性:简单易用,又不失功能强大      

从使用C++语言的角度来看 :引用与指针没有任何的关系 ,引用是变量的新名字,操作引用就是操作对应的变量 

从C++编译器的角度来看 :为了支持新概念“引用”必须要—个有效的解决方案 ,在编译器内部,使用指针常量来实现“引用” ,因此“引用”在定义时必须初始化

在工程项目开发中 

   -当进行C++编程时,直接站在使用的角度看待引用,与指针毫无关系,引用就是变量的别名 

   -当对C++代码进行调试分析时,—些特殊情况,可以考虑站在 C++编译器的角度看待引用 

 

新型类型转换

C方式的强制类型转换 : (Type) (Expression) 、 Type (Expression)

C方式强制类型转换存在的问题 

   -过于粗暴,任意类型之间都可以进行转换,编译器很难判断其正确性 

   -难于定位 ,在源码中无法快速定位所有使用强制类型转换的语句 

// 粗暴的类型转换 test.cpp 
#include <stdio.h>  
  
typedef void(PF)(int);  
  
struct Point  
{  
    int x;  
    int y;  
};  
  
int main()  
{  
    int v = 0x12345;  
    PF* pf = (PF*)v;       
    char c = char(v);      
    Point* p = (Point*)v;  
      
    pf(5);  
      
    printf("p->x = %d\n", p->x);  
    printf("p->y = %d\n", p->y);  

    // 居然能编译通过
  
    return 0;  
} 

                            

C++将强制类型转换分为4种不同的类型 

static_cast:用于基本类型间的转换 ,不能用于基本类型指针间的转换 ,用于有继承关系类对象之间的转换和类指针之间的转换

const_cast:用于去除变量的只读属性 ,强制转换的目标类型必须是指针或引用

reinterpret_cast:用于指针类型间的强制转换 ,用于整数和指针类型间的强制转换

dynamic_cast:用于有继承关系的类指针间的转换 ,用于有交叉关系的类指针间的转换 ,具有类型检查的功能, 需要虚函数的支持 

dynamic_cast要求相关的类中必须有虚函数 ,用于有直接或者间接继承关系的指针(引用)之间 

   指针:  转换成功:得到目标类型的指针 ,转换失败:得到一个空指针 

   引用:  转换成功:得到目标类型的引用 ,转换失败:得到一个异常操作信息

编译器会检查dynamic_cast的使用是否正确 ,类型转换的结果只可能在运行阶段才能得到

// 新式类型转化初探   test.cpp 
#include <stdio.h>  
  
void static_cast_demo()  
{  
    int i = 0x12345;  
    char c = 'c';  
    int* pi = &i;  
    char* pc = &c;  
      
    c = static_cast<char>(i);  
 // pc = static_cast<char*>(pi);  // error
}  
  
void const_cast_demo()  
{  
    const int& j = 1;  
    int& k = const_cast<int&>(j);  
      
    const int x = 2;  
    int& y = const_cast<int&>(x);  
   
//  int z = const_cast<int>(x);  // error  
      
    k = 5;  
      
    printf("k = %d\n", k);  
    printf("j = %d\n", j);  
      
    y = 8;  
      
    printf("x = %d\n", x);  
    printf("y = %d\n", y);  
    printf("&x = %p\n", &x);  
    printf("&y = %p\n", &y);  
}  
  
void reinterpret_cast_demo()  
{  
    int i = 0;  
    char c = 'c';  
    int* pi = &i;  
    char* pc = &c;  
      
    pc = reinterpret_cast<char*>(pi);  
    pi = reinterpret_cast<int*>(pc);  
    pi = reinterpret_cast<int*>(i);  
//  c = reinterpret_cast<char>(i);   // error
}  
  
void dynamic_cast_demo()  
{  
    int i = 0;  
    int* pi = &i;  
    // char* pc = dynamic_cast<char*>(pi);  // error   
}  
  
int main()  
{  
    static_cast_demo();  
    const_cast_demo();  
    reinterpret_cast_demo();  
    dynamic_cast_demo();  
      
    return 0;  
}  

 

// dynamic_cast的使用   test.cpp
#include <iostream>

using namespace std;

class Base
{
public:
    Base()
    {
        cout << "Base::Base()" << endl;
    }

    virtual ~Base()
    {
        cout << "Base::~Base()" << endl;
    }
};

class Derived : public Base
{

};

int main()
{
    Base* p = new Base;

    Derived* pd = dynamic_cast<Derived*>(p);  //编译成功,转换失败返回0

    if( pd != NULL )
    {
        cout << "pd = " << pd << endl;
    }
    else
    {
        cout << "Cast error!" << endl;
    }

    Base* p1 = new Derived();

    Derived* pd1 = dynamic_cast<Derived*>(p1);//转换成功

    if( pd1 != NULL )
    {
        cout << "pd1 = " << pd1 << endl;
    }
    else
    {
        cout << "Cast error!" << endl;
    }

    delete p;
    delete p1;

    return 0;
}

 

 

友元

友元是C++中的—种关系 ,友元关系发生在函数与类之间或者类与类之间 ,友元关系是单项的,不能传递 

        

在类中以friend关键字声明友元 ,类的友元可以是其它类或者具体函数 

友元不是类的—部分 ,友元不受类中访问级别的限制 ,友元可以直接访问具体类的所有成员

#include <stdio.h>  
#include <math.h>  
  
class Point  
{  
    double x;  
    double y;  
public:  
    Point(double x, double y)  
    {  
        this->x = x;  
        this->y = y;  
    }  
      
    double getX()  
    {  
        return x;  
    }  
      
    double getY()  
    {  
        return y;  
    }  
         
    friend double func(Point& p1, Point& p2);  // 在类中用friend关键字对函数或类进行声明
};  
  
double func(Point& p1, Point& p2)  
{  
    double ret = 0;  
      
    ret = (p2.y - p1.y) * (p2.y - p1.y) +  
          (p2.x - p1.x) * (p2.x - p1.x);  // 友元不受类中访问级别的限制
            
    ret = sqrt(ret);  
      
    return ret;  
}  
  
int main()  
{  
    Point p1(1, 2);  
    Point p2(10, 20);  
      
    printf("p1(%f, %f)\n", p1.getX(), p1.getY());  
    printf("p2(%f, %f)\n", p2.getX(), p2.getY());  
    printf("|(p1, p2)| = %f\n", func(p1, p2));  
      
      
    return 0;  
}  

友元关系不具备传递性 ,类的友元可以是其它类的成员函数 

类的友元可以是某个完整的类 ,该类所有的成员函数都是友元

    

B类是C类的友元,A类是B类的友元,不代表A类是C类的友元

#include <stdio.h>  
  
class ClassC  
{  
    const char* n;  
public:  
    ClassC(const char* n)  
    {  
        this->n = n;  
    }  
      
    friend class ClassB;  //类ClassB是ClassC的友元,ClassB的所有成员函数都是ClassC的友元
};  
  
class ClassB  
{  
    const char* n;  
public:  
    ClassB(const char* n)  
    {  
        this->n = n;  
    }  
      
    void getClassCName(ClassC& c)  
    {  
        printf("c.n = %s\n", c.n); //可以直接访问ClassC对象的私有成员 
    }  
      
    friend class ClassA; //ClassA是ClassB的友元 
};  
  
class ClassA  
{  
    const char* n;  
public:  
    ClassA(const char* n)  
    {  
        this->n = n;  
    }  
      
    void getClassBName(ClassB& b)  
    {  
        printf("b.n = %s\n", b.n); //可以直接访问ClassB对象的私有成员  
    }  
    /* 
    void getClassCName(ClassC& c) 
    { 
        printf("c.n = %s\n", c.n);//error,A是B友元,B是C友元,但A不是C友元 
    } 
    */ 
};  
  
int main()  
{  
    ClassA A("A");  
    ClassB B("B");  
    ClassC C("C");  
      
    A.getClassBName(B);  
    B.getClassCName(C);  
      
    return 0;  
}  

 

 

友元是为了兼顾C语言的高效而诞生的 ,友元直接破坏了面向对象的封装性 ,友元在实际产品中的高效是得不偿失的 ,友元在现代软件工程中已经逐渐被遗弃

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值