C++之C语法增强_寄存器_三目_引用初探


站在编译器和C的角度剖析c++原理, 用代码说话


Hello world

首先我们先引入无敌案例之hello world:

#include <iostream>
using namespace std;
int main(void) {
    std::cout << "Hello, World!\n";
    int i = 0;
    cin >> i;
    cout<<"你输入的i是"<<i<<endl;
    return 0;
}

cout就是表示标准输出终端, 但是这里用了<<来表示,我们知道在C中这表示的是移位运算符,但是这里却表示输出重定向?这就说明c++编译器对这个符号做了增强的功能. 这个话题我们在之后的文章中会讲到,这也是个重点.

面向对象VS面向过程

我们都知道c是面向过程的语言,c++是面向对象的语言, 那么到什么是面向过程什么是面向对象呢,对于初学者来说的确很抽象,看到网上的一些概念更加混乱了,那么我们用代码说话:

//面向过程
int main(void){
    double r = 0;
    double s = 0;
    cout << "请你输入圆的半径";
    cin >> r;
    s = 3.14 * r * r;
    cout << "圆的面积是:" << s << endl;
    return 0;
}
//面向对象
struct Circle{
    double m_r;
    double m_s;
    void setR(double r){
        m_r = r;
    }
    double getS(){
        m_s = 3.14*m_r*m_r;
        return m_s;
    }
};
int main(void){

    Circle c1;
    double r = 0;
    cout << "请你输入圆的半径";
    cin >> r;
    c1.setR(r);
    cout << "圆的面积是" << c1.getS() << endl;
    return 0;
}

面向过程的代码我就不解释了, 面向对象的这个代码中首先说的是定义Circle这个结构体数据类型,我们知道数据类型的本质是什么?就是固定大小的内存块的名字而已. 所以我们当然可以在c++中继续使用struct, 但是在c++中struct相比于C的变化是能够在结构体中定义方法,能够在里面继承函数, 这也就是封装的概念. 当然在后面更多的是使用class而不是struct.我们先循序渐进的给大家认识本质,让大家看到c++其实不恐怖. c++中的struct和class还有一点不同是struct中定义的方法默认属性是public, class中默认是private的,别急,这些后面都会涉及到,这里只需要知道class很类似struct就行.
struct类型的加强:
C语言的struct定义了一组变量的集合,C编译器并不认为这是一种新的类型.
C++中的struct是一个新类型的定义声明,进行了功能增强,内部还能加函数.
在c++的struct或class中的这些变量m_r叫做成员变量,void setR(double r){}这些方法叫做成员方法.
在main中,Circle c1;就是用类型定义变量,就是分配了个内存的意思. 只有执行c1.setR(r);c++编译器才会去调用函数.
我们这里介绍个易犯错误模型:

class circle{
public:
    double r;
    double pi = 3.14;
    double area = pi * r * r;
};
int main01_04(void){
    circle p1;
    cout << "请输入area" << endl;
    cin >> p1.r;
    cout << p1.area << endl;
    return 0;
}

这样的结果是个乱码,为什么呢?是不是你也错了?这个错误知识点上面已经提到过了, 再分析一遍: circle p1;分配了内存,好,r因为没有初始化,所以r的内存中是乱码, pi是3.14, 那么现在area的内存中就是乱码. 接着将终端输入给r的内存赋值,好,这时候r的不是乱码了,但是对area并无关系,所以还是乱码. 怎么处理呢?当然是像上面一样在里面写方法来调用.

namespace

这里先说一下每次写c++都要打头写#include <iostream>, 我们知道这是为了包含头文件,但是为啥不写#include iostream.h呢?这是因为当时设计者为了和C有些大点的区别而做的一些并无卵用的活. 所以没多大意义. 并且当我们使用#include <iostream>的时候如果没有写下面的using namespace std;就不能使用好多定义比如cout,你要想使用就得必须写成std::cout这种形式,那这到底是为什么呢?那么我我们看看iostream里面到底是什么鬼?

#ifndef _LIBCPP_IOSTREAM
#define _LIBCPP_IOSTREAM
/*
    iostream synopsis
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>

namespace std {

extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
extern wistream wcin;
extern wostream wcout;
extern wostream wcerr;
extern wostream wclog;

}  // std

*/

#include <__config>
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#ifndef _LIBCPP_HAS_NO_STDIN
extern _LIBCPP_FUNC_VIS istream cin;
extern _LIBCPP_FUNC_VIS wistream wcin;
#endif
#ifndef _LIBCPP_HAS_NO_STDOUT
extern _LIBCPP_FUNC_VIS ostream cout;
extern _LIBCPP_FUNC_VIS wostream wcout;
#endif
extern _LIBCPP_FUNC_VIS ostream cerr;
extern _LIBCPP_FUNC_VIS wostream wcerr;
extern _LIBCPP_FUNC_VIS ostream clog;
extern _LIBCPP_FUNC_VIS wostream wclog;

_LIBCPP_END_NAMESPACE_STD

#endif  // _LIBCPP_IOSTREAM

我们会发现它定义了好多cout, cin等等,但是它本身也没有加using namespace std;这个语句,也就是说要想它本身定义的cout等用起来,就得配合using namespace std;. 好,那么我们就说说这个namespace到底是什么鬼?
namespace就是标准作用域, c++标准库的所有定义都被扔在了一个叫std的namespace中. 在C++中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(命名空间/名字空间/名称空间/名域),可以更好地控制标识符的作用域。std是c++标准命名空间,c++标准程序库中的所有标识符都被定义在std中,比如标准库中的类iostream、vector等都定义在该命名空间中,使用时要加上using声明(using namespace std) 或using指示(如std::string、std::vector<int>).C++命名空间的定义:namespace name { … }.
C++命名空间的使用:
使用整个命名空间:using namespace name;
使用命名空间中的变量:using name::variable;
使用默认命名空间中的变量:::variable;
默认情况下可以直接使用默 认命名空间中的所有标识符

namespace NameSpaceA
{
    int a = 0;
}
namespace NameSpaceB
{
    int a = 1;
    namespace NameSpaceC
    {
        struct Teacher
        {
            char name[10];
            int age;
        };   
    }
}
int main()
{
    using namespace NameSpaceA;
    using NameSpaceB::NameSpaceC::Teacher;  //::域作用符
    printf("a = %d\n", a);
    printf("a = %d\n", NameSpaceB::a);
    Teacher t1 = {"aaa", 3};
    printf("t1.name = %s\n", t1.name);
    printf("t1.age = %d\n", t1.age);
    system("pause");
    return 0;
}

c++之register关键字

register:这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。注意是尽可能,不是绝对。你想想,一个CPU 的寄存器也就那么几个或几十个,你要是定义了很多很多register 变量,它累死也可能不能全部把这些变量放入寄存器吧,轮也可能轮不到你.

int main()
{
    register int a = 0;
    printf("&a = %x\n", &a);
    system("pause");
    return 0;
}

早期C语言编译器不会对代码进行优化,因此register变量是一个很好的补充.
c++中的编译器对register进行了功能增强, 即使没有使用register修饰变量,c++也会自动优化常用的变量.

int main(void){
    register int i = 0;
    int b = 0;
    for (i = 0; i < 10000; i++) {
        ;
    }
    printf("%d \n", &i);
    return 0;
}

register关键字 请求编译器让变量i直接放在寄存器里面,速度快, 在c语言中 register修饰的变量 不能取地址,但是在c++里面做了内容. register关键字请求”编译器”将局部变量存储于寄存器中, C语言中无法取得register变量地址, 在C++中依然支持register关键字, C++编译器有自己的优化方式,不使用register也可能做优化,会将经常使用的变量自动放在register中. C++中可以取得register变量的地址, 但是C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。
但是使用register修饰符有几点限制:
首先,register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数.
其次,因为register变量可能不存放在内存中,所以不能用”&”来获取register变量的地址。由于寄存器的数量有限,而且某些寄存器只能接受特定类型的数据(如指针和浮点数),因此真正起作用的register修饰符的数目和类型都依赖于运行程序的机器,而任何多余的register修饰符都将被编译程序所忽略。
在某些情况下,把变量保存在寄存器中反而会降低程序的运行速度。因为被占用的寄存器不能再用于其它目的;或者变量被使用的次数不够多,不足以装入和存储变量所带来的额外开销。
早期的C编译程序不会把变量保存在寄存器中,除非你命令它这样做,这时register修饰符是C语言的一种很有价值的补充。然而,随着编译程序设计技术的进步,在决定那些变量应该被存到寄存器中时,现在的C编译环境能比程序员做出更好的决定。实际上,许多编译程序都会忽略register修饰符,因为尽管它完全合法,但它仅仅是暗示而不是命令。

c++之boolean运算

C++在C语言的基本类型系统之上增加了bool, C++中的bool可取的值只有true和false, 理论上bool只占用一个字节,如果多个bool变量定义在一起,可能会各占一个bit,这取决于编译器的实现. true代表真值,编译器内部用1来表示, false代表非真值,编译器内部用0来表示. bool类型只有true(非0)和false(0)两个值. C++编译器会在赋值时将非0值转换为true,0值转换为false.

int main(void){
    bool b1;
    b1 = 4;
    printf("b1 = %d\n", b1);//1
    b1 = -1;
    printf("b1 = %d\n", b1);//1
    b1 = 0;
    printf("b1 = %d\n", b1);
    int a;
    bool b = true;
    printf("b = %d, sizeof(b) = %d\n", b, sizeof(b));//1, 1
    b = 4;
    a = b;
    printf("a = %d, b = %d\n", a, b);//1, 1
    b = -4;
    a = b;
    printf("a = %d, b = %d\n", a, b);//1, 1
    a = 10;
    b = a;
    printf("a = %d, b = %d\n", a, b);//10, 1
    b = 0;
    printf("b = %d\n", b);//0
    return 0;
}

c++之三目运算

首先我们考虑C中的三目运算符:

int main()
{
    int a = 10;
    int b = 20;
    (a < b ? a : b )= 30;
    printf("a = %d, b = %d\n", a, b);
    system("pause");
    return 0;
}

这个在C编译器是报错的,三目运算符是一个表达式 ,表达式不可能做左值. (a < b ? a : b )是一个表达式, 表达式的值在寄存器中,寄存器中是没有地址的,给一个没有地址的存储区赋值肯定gg. 但是在c++中可以, 说明c++编译器进行了功能增强, 返回的是a本身. 元素当左值的必要条件就是,元素有地址空间.那么我们就能这样的改这个代码在C中也能实现左值功能:
*(a < b ? &a : &b) = 30;, 所以说C++到底干了个啥,其实就是干了这个.
比如说(a < b ? 100 : b)=30这就错了,100的地址是什么鬼?所以说三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用,说白了就是没地址, 回归之前提到的元素当左值的必要条件就是,元素有地址空间.
总结一下:
C语言中的三目运算符返回的是变量值,不能作为左值使用, C++中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方,抛砖引出了我们下面讨论以及下一篇文章主要讨论的重点—引用.

c++之const

const在C中其实是个”冒牌货”,让我们开一段C:

int main()
{
    const int a = 10;
    int *p = (int*)&a; 
    printf("a===>%d\n", a);
    *p = 11;
    printf("*p===>%d\n", *p);//11
    printf("a===>%d\n", a);//11
    return 0;
}

在C中虽然用const定义了a, 也就是说我们不能直接去修改a的值,a变成了只读的, 但是我们通过指针p指向a的地址,然后间接的去修改a的地址指向的内存空间, 那么a的值也就变了.是不是感觉被骗了的不爽. 但是在c++中上面的这段代码就是a返回的依然是10. 这是为什么呢?首先能分析出来的是*p所指的内存空间和&a不一样. 这里解释一下c++编译器对const做了什么手脚:
C语言中的const变量, C语言中const变量是只读变量,有自己的存储空间. 然后在c++中, 一旦用const神明内部就维护了一个符号表, a->10. 这个也是只读的. 编译过程中若发现使用常量则直接以符号表中的值替换, 编译过程中若发现对const使用了extern或者&操作符,则给对应的常量分配存储空间(兼容C), 也就是说C++中的const常量, 并不会给分配空间而是仅仅保存在了符号表中, 只有在&或extern的时候,才会给分配个空间供你随便玩,但是这个空间是不会对之前符号表中的值造成任何影响的. c++中的const修饰的,是一个真正的常量,编译期间就定下来了, 而不是C中变量(只读). 那就有一个问题,这岂不是和宏定义一样了,的确是这样,它们很想,但是也是有不同的:
c++中的const常量类似于宏定义const int c = 5; ≈ #define c 5, C++中的const常量在与宏定义不同, const常量是由编译器处理的,提供类型检查和作用域检查, 宏定义由预处理器处理,单纯的文本替换.

void fun1()
{
    #define a 10//从这行往下所有的代码都能使用a
    const int b = 20;//只有在作用域内能使用
    //#undef//但是也是可以卸载掉
}
void fun2()
{
    printf("a = %d\n", a);
    //printf("b = %d\n", b);
}

const int a = 1;int const b = 1;是一样的.

char buf[20];
getMem(getMem(&buf));

这种情况下C语言是通过的,但是在c++中是不通过的. 本身这个语句就是很危险的, 因为这里其实char **char (*)[20]是不一样的. char (*)[20]是指向数组的指针, 你一旦写buf = buf + 1; 然后getMem(&buf)就gg. 所以千万不要将buf地址直接放进去了,一般都是定义一个指针char *p = NULL. 然后再传入p的地址在函数中分配.
还有是区分一些:const Teacher *p, Teacher *const p:
Teacher *const p:表示常指针,表示这个指针只能指向一个地方,也就是p不能再赋值其他的地址.
const Teacher *p:表示指向常量的指针, 也就是说这个指针指向的常量不能再赋值其他. *p不能再进行赋值.

c++之引用初探

变量名实质上是一段连续存储空间的别名,是一个标号(门牌号), 程序中通过变量来申请并命名内存空间, 通过变量的名字可以使用存储空间. 那么对一段连续的内存空间只能取一个别名吗?在c++中当然不是的.
引用是C++的概念,属于C++编译器对C的扩展.引用基于C的本质就是它是个常指针.

nt main()
{
    int a = 0;
    int &b = a; //== int * const b = &a 
     b = 11;  //== *b = 11;
    return 0;
}

请不要用C的语法考虑, 引用可以看作一个已定义变量的别名, 引用作为函数参数声明时不进行初始化.

nt main()
{
    int a = 10;
    int &b = a;
    //b是a的别名,请问c++编译器后面做了什么工作?
    b = 11;
    cout<<"b--->"<<a<<endl;
    printf("a:%d\n", a);
    printf("b:%d\n", b);
    printf("&a:%d\n", &a);
    printf("&b:%d\n", &b);//请思考:对同一内存空间可以取好几个名字吗?
    system("pause");
    return 0;
}

普通引用在声明时必须用其它的变量进行初始化, 为什么要初始化呢,就是因为上面提到的引用的本质就是常指针,const定义的能不进行初始化吗?
引用作为其它变量的别名而存在,因此在一些场合可以代替指针, 引用相对于指针来说具有更好的可读性和实用性.

int swap1(int &a, int &b){
    int tmp = a;
    a = b;
    b = tmp;
    return 0;
}
int swap2(int *a, int *b){
    int tmp = *a;
    *a = *b;
    *b = tmp;
    return 0;
}

那么普通的引用有自己独立的空间吗?当然是有的,让我们敲一下这段代码:

struct Teacher {
    int &a;
    int &b;
};
int main()
{
    printf("sizeof(Teacher) %d\n", sizeof(Teacher));//8
    return 0;
}

有人在这里会问为什么是8,为什么不需要初始化?不是说需要初始化吗?首先这是常指针是个指针,所以8就知道了吧,初始化的问题,结构体的定义,只能告诉编译器,里面有什么类型的数据,占有多少字节,是不能在里面赋值的。构造函数是类定义里的,在那里面是可赋初值的. 也就是说参数列表,类成员中是可以不初始化话的,在调用函数, 构造对象时才算初始化.
从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏.
C中间接赋值的三个条件:
1. 定义两个变量 (一个实参一个形参)
2. 建立关联 实参取地址传给形参
3. *p形参去间接的修改实参的值.
那么引用就是将后两步c++编译器帮我们手动取了一个实参地址, 传给了形参引用(常指针).

struct Teacher{
    char name[64];
    int age;
};
//以前我们这样写
void printTe(Teacher *p){
    printf("%d\n", p->age);
}
//现在我们用引用了
void printTe(Teacher &t2){//t2就是t1的别名,不存在深浅拷贝问题
    printf("%d\n", t2.age);
}
int main(void){
    int a = 10;//c编译器分配四个空间的内存,能不能在这块内存给a再取一个别名
    //引用的语法:Type& name = var;
    int &b = a;//表示b就是a的别名
    a = 11;//直接赋值
    {
        int  *p = &a;
        *p = 12;//C中的间接赋值
    }
    b = 14;//这样b和a都是14了

    Teacher t1;
    Teacher &t2 = t1;
    t1.age = 10;
    printTe(&t1);
    printTe(t1);
}

容我给你分析一下引用的过程: 当Teacher &t2 = t1;时,进行的内部操作就是Teacher * const t1 = &t1., 然后执行printTe(t1);的时候, 调到了函数参数列表那里,void printTe(Teacher &t2){C++编译器发现呀?是个引用类型,那我给你转一下: Teacher * const t2. 所以printTe(t1);这一步就等价于printTe(&t1)过来了. 然后在函数内部编译器又自动给我们取地址间接改变了值. (注意常指针是可以取改变指针指向的内存空间的值的).
引用一旦生出来,就必须给它赋个值, 就类似于buffer, char buf[20];一出现就是个常指针(注意与常量指针是不同的).初值就是内存首地址.


联系方式: reyren179@gmail.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 第1篇初级篇 第1章 初识C++ 1.1 c++简介 1.2 C++与C的区别 1.3 学习c++之前需要先学C吗 1.4 c++与其他语言的区别 1.5 c++的版本以及安装问题 第2章 做一个最简短的C4-+程序 2.1 简单的屏幕输出小程序 2.2 输出语句的使用 2.3 std::介绍 2.4 iostream与iostream.h的区别 2.5 重名问题 2.6 注释 2.7 总结 第3章 初步了解函数 3.1 一个简单的函数 3.2 函数的传参 3.3 函数的返回值、参数与变量 3.4.函数的声明与定义 3.5 局部变量 3.6 全局变量 3.7 总结 第4章 C4-+数据类型 4.1 变量的定义 4.2 将变量及数据存储在内存中 4.3 布尔型变量 4.4 字符型变量 4.5 wchart双字符型变量 4.6 整型概述 4.7 整型变量的定义 4.8 浮点型变量 4.9 常量 4.10枚举型常量 第5章 if语句与运算符 5.1 语句的定义 5.2 块的定义 5.3 表达式的定义 5.4 运算符的定义 5.4.1 赋值运算符的定义 5.4.2 数学运算符的定义 5.4.3 赋值运算符与数学运算符的联合 5.5 自加与自减 5.5.1 前置 5.5.2 后置 5.6 表达式的优先级 5.7 关系运算符 5.8 if语句 5.8.1 else语句 5.8.2 elseif语句 5.8.3 if语句的嵌套 5.9 逻辑运算符及其使用 5.9.1 逻辑“与” 5.9.2 逻辑“或” 5.9.3 逻辑“非” 5.9.4 逻辑运算符的优先级 5.9.5 运算式的真假关系 5.1 0三目运算符 5.1 0.1 三目运算符的优先问题 5.1 0.2 三目运算符的使用问题 5.1 0.3 三目运算符的型别问题 5.1 0.4 三目运算符在字符型变量中的应用 5.1 1复杂嵌套的if语句 5.1 2总结 第6章 面向对象 6.1 面向对象程序语言的主要特征 6.2 类、对象和成员 6.3 类、对象和成员的使用方法及区别 6.3.1 声明一个类 6.3.2 命名习惯 6.3.3 定义一个对象 6.3.4 类与对象的区别 6.3.5 对象与成员的关系 6.3.6 不要给类赋值 6.3.7 对象只能调用类中存在的方法 6.4 公有 6.5 私有 6.6 成员函数的声明和定义 6.7 内联函数 6.7.1 普通内联函数 6.7.2 内联成员函数 6.8 头文件与源文件 6.9 const成员函数 6.10构造函数 6.11默认构造函数 6.12析构函数 6.13析构对象数组 6.14总结 第7章 循环语句 7.1 循环语句的前身——goto语句 7.2 慎用goto语句 7.3 while语句 7.3.1 带运算符的while语句 7.3.2 以字符为条件的while语句 7.3.3 限定while循环的次数 7.3.4 continue语句 7.3.5 break语句 7.3.6 永不休止的while循环 7.4. do……while循环 7.5 for循环 7.5.1 灵活的for循环 7.5.2 条件为空的for循环 7.5.3 执行为空的for循环 7.5.4 嵌套的for循环 7.6 switch语句 7.6.1 switch语句常见错误 7.6.2 switch的菜单功能 7.7 总结 第8章 指针 8.1 什么是地址 8.2 用指针来保存地址 8.2.1 空指针 8.2.2 指针与变量类型 8.2.3 用指针来访问值 8.2.4 指针地址、指针保存的地址和 该地址的值 8.2.5 指针对数值的操作 8.2.6 更换指针保存的地址 8.3 为什么使用指针 8.3.1 栈和堆 8.3.2 用指针创建堆中空间 8.3.3 用指针删除堆中空间 8.4 动态内存 8.4.1 内存泄漏 8.4.2 在堆中创建对象 8.4.3 在堆中删除对象 8.4.4 访问堆中的数据成员 8.4..5 在构造函数中开辟内存空间 8.4.6 对象在栈与堆中的不同 8.5 this指针 8.6 指针的常见错误 8.7 指针运算 8.7.1 指针的加减运算 8.7.2 指针的赋值运算 8.7 _3指针的相减运算 8.7.4 指针的比较运算 8.8 指针 8.8.1 常量指针 8.8.2 指向常量的指针 8.8.3 指向常量的常指针 8.9 总结 第9章 引用
目录 1、 C++对C的扩展 1 1简单的C++程序 1 1.1求圆的周长和面积 1 1.2初学者易犯错误模型 3 2程序设计方法的发展历程 4 3 C语言和C++语言关系 6 4 C++对C的加强 6 4.1 namespace命名空间 6 4.2 “实用性”增加 6 4.3 register关键字增强 6 4.4变量检测增强 6 4.5 struct类型加强 6 4.6 C++中所有的变量和函数都必须有类型 6 4.7新增Bool类型关键字 6 4.8三目运算符功能增强 6 5 C/C++中的const 6 1 const基础知识(用法、含义、好处) 6 2 C中“冒牌货” 6 3 const和#define相同之处 6 4 const和#define的区别 6 5 结论 6 6引用专题讲座 6 1引用(普通引用) 6 2常引用 6 3 const引用结论 6 4const修饰类 6 5综合练习 6 7C++对C的函数扩展 6 1 inline内联函数 6 2 默认参数 6 3 函数占位参数 6 4 默认参数和占位参数 6 5 函数重载(Overroad) 6 8附录 6 附录1:C++语言对C语言扩充和增强的几点具体体现 6 附录2:C语言register关键字—最快的关键字 6 一、皇帝身边的小太监----寄存器 6 2、类和对象 6 1前言 6 2类和对象 6 2.1 基本概念 6 2.2类的封装 6 2.3 C++面向对象程序设计举例 6 2.4 作业 6 3对象的构造和析构 6 3.1构造和析构函数 6 3.2构造函数的分类及调用 6 3.3构造函数调用规则研究 6 3.4深拷贝和浅拷贝 6 3.5多个对象构造和析构 6 3.6构造函数和析构函数的调用顺序研究 6 3.7构造函数和析构函数综合练习 6 3.8 对象的动态建立和释放 6 4静态成员变量成员函数 6 4.1静态成员变量 6 4.2静态成员函数 6 4.3综合训练 6 5 C++面向对象模型初探 6 5.1基础知识 6 5.2编译器对属性和方法的处理机制 6 5.3总结 6 5.4 this指针 6 5.5全局函数PK成员函数 6 6 6友元 6 6.1友元函数 6 6.2友元类 6 7强化训练 6 1 static关键字强化训练题 6 2 数组类封装 6 3小结 6 8运算符重载 6 8.1概念 6 8.2运算符重载的限制 6 8.3运算符重载编程基础 6 8.4运算符重载提高 6 8.5运算符重载在项目开发中的应用 6 8.7附录:运算符和结合性 6 2、 继承和派生 6 3.1继承概念 6 3.1.1类之间的关系 6 3.1.2继承关系举例 6 3.1.3 继承相关概念 6 3.1.4 派生类的定义 6 3.1.5 继承重要说明 6 3.2派生类的访问控制 6 3.2.1单个类的访问控制 6 3.2.2不同的继承方式会改变继承成员的访问属性 6 3.2.3“三看”原则 6 3.2.3派生类类成员访问级别设置的原则 6 3.2.4综合训练 6 3.3继承中的构造和析构 6 3.3.1类型兼容性原则 6 3.3.2继承中的对象模型 6 3.3.3继承中的构造析构调用原则 6 3.3.4继承与组合混搭情况下,构造和析构调用原则 6 3.3.5继承中的同名成员变量处理方法 6 3.3.6派生类中的static关键字 6 3.4多继承 6 3.4.1多继承的应用 6 3.4.2虚继承 6 3.5继承总结 6 4、多态 6 4.1多态 6 4.1.1问题引出 6 4.1.2面向对象新需求 6 4.1.3解决方案 6 4.1.4多态实例 6 4.1.5多态工程意义 6 4.1.6多态成立的条件 6 4.1.7多态的理论基础 6 4.2多态相关面试题 6 面试题1:请谈谈你对多态的理解 6 面试题2:谈谈C++编译器是如何实现多态 6 面试题3:谈谈你对重写,重载理解 6 #include <cstdlib> 6 #include <iostream> 6 using namespace std; 6 class Parent01 6 { 6 public: 6 Parent01() 6 { 6 cout<<"Parent01:printf()..do"<<endl; 6 } 6 public: 6 virtual void func() 6 { 6 cout<<"Parent01:void func()"<<endl; 6 } 6 virtual void func(int i) 6 { 6 cout<<"Parent:void func(int i)"<<endl; 6 } 6 virtual void func(int i, int j) 6 { 6 cout<<"Parent:void func(int i, int j)"<<endl; 6 } 6 }; 6 class Child01 : public Parent01 6 { 6 public: 6 //此处2个参数,和子类func函数是什么关系 6 void func(int i, int j) 6 { 6 cout<<"Child:void func(int i, int j)"<<" "<<i + j<<endl; 6 } 6 //此处3个参数的,和子类func函数是什么关系 6 void func(int i, int j, int k) 6 { 6 cout<<"Child:void func(int i, int j, int k)"<<" "<<i + j + k<<endl; 6 } 6 }; 6 void run01(Parent01* p) 6 { 6 p->func(1, 2); 6 } 6 int main() 6 { 6 Parent01 p; 6 p.func(); 6 p.func(1); 6 p.func(1, 2); 6 Child01 c; 6 //c.func(); //问题1 6 c.Parent01::func(); 6 c.func(1, 2); 6 run01(&p); 6 run01(&c); 6 system("pause"); 6 return 0; 6 } 6 //问题1:child对象继承父类对象的func,请问这句话能运行吗?why 6 //c.func(); //因为名称覆盖,C++编译器不会去父类中寻找0个参数的func函数,只会在子类中找func函数。 6 //1子类里面的func无法重载父类里面的func 6 //2当父类和子类有相同的函数名、变量名出现,发生名称覆盖(子类的函数名,覆盖了父类的函数名。) 6 //3//c.Parent::func(); 6 //问题2 子类的两个func和父类里的三个func函数是什么关系? 6 面试题4:是否可类的每个成员函数都声明为虚函数,为什么。 c++编译器多态实现原理 6 面试题5:构造函数中调用虚函数能实现多态吗?为什么? c++编译器多态实现原理 6 面试题6:虚函数表指针(VPTR)被编译器初始化的过程,你是如何理解的? 6 面试题7:父类的构造函数中调用虚函数,能发生多态吗? c++编译器多态实现原理 6 面试题8:为什么要定义虚析构函数? 6 其他 6 4.3多态原理探究 6 4.3.1 多态的实现原理 6 4.3.2如何证明vptr指针的存在 6 4.3.3构造函数中能调用虚函数,实现多态吗 6 5、纯虚函数和抽象类 6 5.1基本概念 6 5.2抽象类案例 6 5.3抽象类在多继承中的应用 6 5.3.1有关多继承的说明 6 5.3.2多继承的应用场景 6 5.4抽象类知识点强化 6 5.5面向抽象类编程思想强化 6 5.4.1案例:socket库c++模型设计和实现 6 5.4.2案例:计算员工工资 6 5.4.3案例:计算几何体的表面积和体积 6 5.6 C面向接口编程和C多态 6 5.6.1函数类型语法基础 6 5.6.2函数指针做函数参数 6 5.6.3函数指针正向调用 6 5.6.4函数指针反向调用 6 5.6.5.C动态库升级成框架案例 6 5.6.6附录:诸葛亮的锦囊妙计 6

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值