`
zhangjia328
  • 浏览: 17973 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

C++语法小结: (1)

    博客分类:
  • C++
阅读更多

1. 虚函数

1.1 触发动态绑定(调用虚函数)的条件

      <C++ Primer>中说触发虚函数有两个条件:一是所调用的成员函数为虚函数,二是必须通过基类类型的引用或指针进行函数调用。

      但是,在vc2005中,如下的代码并不能触发动态绑定:

      base_t objBase;

      drived_t objDrived;

      base_t &pObj = objBase;

      pObj.vfcn();

      pObj = objDrived;

      pObj.vfcn();

      实际上,两次对vfcn()的调用都是调用的base_t中的版本,编译器在编译期间已经将调用的函数确定为基类base_t中的版本,并没有生成动态绑定的版本。

      如果不是对基类引用pObj赋值、而是将其用作函数的形参的话,那么将触发动态绑定,如下:

      void fcn(base_t & pObj){

           pObj->vfcn();

      }

      void main(){

           base_t objBase;

            drived_t objDrived;

           fcn(objBase); // 调用fcn()的基类版本

            fcn(objDrived); // 调用fcn()的派生类版本

      }

2. 类的作用域

2.1 对象的成员函数可以访问当前对象(this指针指向的对象)的protectedprivate成员,而且可以访问同类型的其他对象的protectedprivate成员。如下:

      class cls_t {

public: 

            void ChgOtherVal(cls_t &obj){obj.v = ... }

pirvate:

            int v;

}

ChgOtherVal()中,可以通过 obj访问 objprotectedprivate成员。

      可以这样理解,无论是当前对象(this指针指向的对象)还是同类型的其他对象(比如上例中的obj),都属于同一个类类型,理所当然在类的作用域下可以访问他们的所有成员。只是,当前对象通过隐含的this指针访问,而其他同类型的对象是通过显式的函数参数(指针或者引用)进行访问,本质上都是该类类型的对象,所以类的成员函数对他们的访问权限是一样的。

3. 复制初始化

      <C++ Primer>中说复制初始化使用“=”,“复制初始化首先使用指定的构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象”。但实际上,仅仅当“=”右操作数为同类型对象时,才会调用复制构造函数,否则,调用相应的构造函数直接初始化左操作数(正在创建的对象)。

class Cls_t {

public:

      Cls_t(int v=0): val(v) {cout << "I'm Cls_t(int=0)" << endl;}   

      Cls_t(Cls_t &obj): val(obj.val) {cout << "I'm Cls_t(Cls_t &)" << endl;}

      void Print() const { cout << "val=" << val << endl; }

private:

      int val; 

};

 

int main() {

      Cls_t obj = 100; // 仅仅调用了Cls_t(int=0) ,直接对 obj进行初始化

      obj.Print();

      Cls_t obj2 = obj; // 调用 Cls_t(Cls_t &) obj2 进行初始化

      obj2.Print();

}

      同样,<C++ Primer>中说“如果使用常规的花括号括住的数组初始化列表来提供类类型元素的显示的初始化,则使用复制初始化来初始化每个元素。”但是实际上并没有用复制初始化,而是根据初始化列表的类型调用相应的构造函数直接对类类型元素进行初始化。代码如下:

int main() {

      ... ...

      Cls_t objAry [] = {1, 2, 3};

}

      定义 objAry的语句产生的输出为:

I'm Cls_t(int=0)

I'm Cls_t(int=0)

I'm ls_t(int=0)

根本没有调用Cls_t的复制构造函数。

 

4. 初始化与赋值的区别

class Cls_t {

public:

      Cls_t(int v=0): val(v) {}    

      Cls_t(Cls_t &obj): val(obj.val) {cout << "I'm Cls_t(Cls_t &)" << endl;}

      void Print() const { cout << "val=" << val << endl; }

      Cls_t &operator=(const Cls_t & obj) {

           cout << "I'm operator=(const Cls_t &)" << endl;

           val=obj.val; return *this;        

      }

private:

      int val; 

};

 

int main() {

      Cls_t obj1 = 100;  

      Cls_t obj2 = 200;       

      Cls_t obj3 = obj1;  // 初始化,所以调用“复制构造函数”

      obj3 = obj2;          // 赋值,将调用“赋值操作符”

}

 

5. 友元与继承

      “友元关系不能继承,基类的友元对派生类的成员没有特殊的访问权限”,但是,基类的友元对派生类中继承的基类成员有特殊的访问权限。

class Cls10_t{

      friend class Cls20_t;

public:

      Cls10_t(int v=100): val(v) {}  

private:

      int val;

};

 

class Cls11_t: public Cls10_t{

public:

      Cls11_t(int v=200): myVal(v) {}

private:

      int myVal;

};

 

class Cls20_t {

public:

      void PrintVal(Cls10_t &obj) const {

cout << "I am in Cls20_t. Cls10_t::val=" << obj.val << endl;

}

      void PrintVal(Cls11_t &obj) const {

           cout << "I am in Cls20_t. Cls11_t::val=" << obj.val

                 //<< ", Cls11_t::myVal=" << obj.myVal  // 不能访问!

                 << endl;

      }

};

在上面的例子中,Cls20_t可以访问Cls10_t类型对象的私有成员和Cls11_t类型对象中继承自Cls10_t的成员,但是对在Cls11_t中定义的非公有成员没有特殊的访问权限。

 

“如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。” 与上面同样道理,派生类继承自基类的函数仍然像基类一样访问授予友元关系的类,但是在派生类中自己定义的接口对授予友元关系的类没有特殊的访问权限。如下:

class Cls10_t{

      friend class Cls20_t;

public:

      Cls10_t(int v=100): val(v) {}  

private:

      int val;

};

 

class Cls20_t {

public:

      void PrintVal(Cls10_t &obj) const { cout << "I am in Cls20_t. Cls10_t::val=" << obj.val << endl;}

};

 

class Cls21_t: public Cls20_t{

public:

//   void MyPrintVal(Cls10_t &obj) const {cout << "I am in Cls21_t. Cls10_t::val=" << obj.val << endl;}

};

 

int main() {

      Cls10_t obj10;

      Cls20_t obj20;

      Cls21_t obj21;

 

      obj20.PrintVal(obj10);

      obj21.PrintVal(obj10);

      //obj21.MyPrintVal(obj10);

}

在上面的例子中,Cls21_t继承自Cls20_t的接口可以访问Cls10_t中的私有成员,但是,Cls21_t自己定义的接口对Cls10_t没有特殊访问权限。

 

6. 派生类中的复制控制

      如果派生类中定义了赋值操作符,那么该赋值操作符应该显式地调用基类部分的赋值操作符(此时,如果基类没有自己定义赋值操作符,将使用合成的赋值操作符),否则将导致不一致的情况:派生类部分被赋值,基类部分没有被赋值。如下:

class Cls1_t {

public:

      Cls1_t(int v=0): val(v){}

protected:

      int val;

};

 

class Cls2_t: public Cls1_t {

public:

      Cls2_t(int dv=0, int v=0): dVal(dv), Cls1_t(v){}

      Cls2_t &operator=(Cls2_t & obj){

           //Cls1_t::operator=(obj);

           dVal = obj.dVal;

           return *this;

      }

      void Print() const { cout << "val=" << val << ", dVal=" << dVal << endl;}

private:

      int dVal;

};

 

int main(){

      Cls2_t obj1;

      obj1.Print();    

      Cls2_t obj2(200, 100);

      obj2.Print();

      obj1 = obj2;

      obj1.Print();

}

 

Cls2_t的赋值操作符并没有显示调用基类部分的赋值操作符,则程序输出为:

      val=0, dVal=0

      val=100, dVal=200

      val=0, dVal=200

如果Cls2_t的赋值操作符的控制体上加上:Cls1_t::operator=(obj),那么程序输出为:

      val=0, dVal=0

      val=100, dVal=200

      val=0, dVal=200

     

      与赋值操作符类似,如果派生类中定义了复制构造函数,那么该复制构造函数应该显示地调用基类部分的复制构造函数,否则,编译器将调用基类的默认构造函数,从而导致不一致的情况。

      但是,派生类的析构函数不应该显式调用基类部分的析构函数,因为,在调用派生类的析构函数之后,编译器将自动调用基类部分的析构函数。在派生类的析构函数中显式调用基类部分的析构函数导致基类部分的析构函数被重复调用,可能引起错误。如下:

class Cls1_t {

public:

      ~Cls1_t() { cout << "I'm Cls1_t's destructor" << endl;}

};

 

class Cls2_t: public Cls1_t {

public:

      ~Cls2_t() {          

           cout << "I'm Cls2_t's destructor" << endl;

           Cls1_t::~Cls1_t();  // 显式调用基类部分的析构函数

      }

};

 

int main(){

      Cls2_t obj;

}

 

程序输出为:

      I'm Cls2_t's destructor

      I'm Cls1_t's destructor

      I'm Cls1_t's destructor

基类部分的析构函数被重复调用。

分享到:
评论

相关推荐

    深入理解C++11:C++11新特性解析与应用

    2547.4 本章小结 256第8章 融入实际应用 2588.1 对齐支持 2588.1.1 数据对齐 2588.1.2 C++11的alignof和alignas 2618.2 通用属性 2678.2.1 语言扩展到通用属性 2678.2.2 C++11的通用属性 2688.2.3 预定义的通用属性 ...

    less语法小结-源代码+注释.rar

    此工程是在b站学习了尚硅谷less教学视频后的语法小结。在此作为一种分享与个人的记录。 关键字:less变量的常见用途,less的混合,less的继承,避免编译less,使用less进行加减乘除。

    c++ Builder+实例入门陈雪飞清晰版

    附录A C++语法基础 A.1 C++的基本数据类型及常量、变量 A.1.1 常用的基本数据类型 A.1.2 变量的定义 A.1.3 常量的定义 A.2 操作符、表达式及语句 A.2.1 操作符 A.2.2 表达式 A.2.3 ...

    C++编程思想习题

    1.7小结 第2章 数据抽象 2.1声明与定义 2.2一个袖珍C库 2.3放在一起:项目创建工具 2.4什么是非正常 2.5基本对象 2.6什么是对象 2.7抽象数据类型 2.8对象细节 2.9头文件形式 2.10嵌套结构 2.11小结 2.12练习 第3章 ...

    Visual C++2010开发权威指南(共三部分).part1.rar

    2.9 小结 102 第3章 Visual C++ 2010 MFC菜单编程 103 3.1 菜单编程 103 3.1.1 创建菜单 104 3.1.2 创建菜单热键 115 3.1.3 标记菜单 115 3.1.4 给菜单加入图标 119 3.1.5 禁用菜单 121 3.1.6 移除与加载菜单 127 ...

    传智播客_C++基础课程讲义_v1.0.7

    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类之间...

    Visual C++2010开发权威指南.part07

    第15章 Visual C++ 2010 MFC网络 第15章 程序设计 580 15.1 计算机网络的基础知识 580 15.1.1 TCP/IP协议模型 580 15.1.2 IP地址 582 15.1.3 端口 582 15.1.4 数据封装 582 15.2 Winsock简介 583 ...17.10 小结 670

    McGraw C++程序调试实用手册

    内容简介 · · · · · ·  本书作为有关 Visual C++ Debugger的专著,是一本非常难得的好书。书中深入地分析了开发不包含逻辑和语法错误的代码技巧以及调试程序的基本原理,介绍了开发和调试命令行...17.3 小结

    C/C++指针小结

    第一章。指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值...从语法的角度看,你只要把指针声明

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    1.7 小结 27 1.8 本章主要内容 28 第2章 数据、变量和计算 29 2.1 C++程序结构 29 2.1.1 main()函数 36 2.1.2 程序语句 36 2.1.3 空白 38 2.1.4 语句块 38 2.1.5 自动生成的控制台程序 39 2.2 定义变量 40 ...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar

    10.8 有关指针的数据类型和指针运算的小结 167 10.8.1 有关指针的数据类型的小结 167 10.8.2 指针运算的小结 167 10.8.3 void 指针类型 168 11 结构体与共用体 11.1 定义一个结构的一般形式 170 11.2 结构类型变量的...

    Visual C++开发实战1200例 第二章

    2.1 基本语法 实例032输出问候语 实例033输出带边框的问候语 实例034不同类型数据的输出 实例035输出字符表情, 实例036获取用户输入的用户名 2.2 运算符的妙用 实例037简单的字符加密 实例038实现两个变量的互换 ...

    编译原理实验 语法分析程序设计(报告+源代码)

    包含源代码+语法分析程序设计思路+流程图+数据结构+错误恢复策略+结果分析+小结 实验内容如下: 对于只含有+、*运算的算术表达式,编写相应的语法分析程序,要求: 1. 用表驱动的预测分析法进行语法分析。 2. 采用...

    C/C++常用算法手册.秦姣华(有详细书签).rar

    如果读者采用其他编程语言,例如C++、C#、VB、Java等,根据其语法格式进行适当的修改即可。 《C/C++常用算法手册 》主要定位于有一定C/C++语言编程基础、想通过学习算法与数据结构提升编程水平的读者,也可作为...

    XML高级编程

    2.11 XML语法小结 43 2.12 格式正规的文档 44 2.13 解析器 45 2.13.1 事件驱动的解析器 45 2.13.2 基于树的解析器 46 2.13.3 解析器基准测试 46 2.14 书籍目录应用程序 47 2.15 小结 49 第3章 文档类型定义 51 3.1 ...

    数据结构(C++)有关练习题

    实验一 复习C++有关知识 实验目的: 通过实验掌握下列知识: 1、复习C++有关基本知识; 2、熟悉VC编程、编译和调试环境; 内容及步骤: 编写一个类Complex,定义复数的加法、减法、乘法和除法运算,...

    iPhone应用开发从入门到精通代码

    C 2.0编程语言232.3.1 Objective-C简介232.3.2 Objective-C的发展历史242.4 混用C/C++编程242.5 本章小结252.6 本章练习25第3章 使用苹果公司提供的开发工具263.1 安装Xcode开发工具263.1.1 从操作系统光盘...

Global site tag (gtag.js) - Google Analytics