`
clskkk2222
  • 浏览: 34903 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论
  • NeuronR: 引用因为用于向函数传递对象和从函数返回对象,该构造函数一般不应 ...
    复制构造函数

名字查找 构造函数 explicit

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

每个类都定义了自己的新作用域和唯一的类型,两个不同的类具有两个的类作用域

 

即使两个类具有完全相同的成员列表,它们也是不同的类型,每个类的成员不同于任何其他类(或任何其他作用域)的成员

 

在类作用域之外,成员只能通过对象或指针分别使用成员访问操作符 . 或 -> 来访问

.操作符左边的操作数是一个类对象

->操作符左边的操作数是指向类对象的指针

 

形参表和函数体处于类作用域中
在定义于类外部的成员函数中,形参表和成员函数体都出现在成员名之后,这些都是在类作用域中定义,所以可以不用限定而引用其他成员

函数返回类型不一定在类作用域中


如果函数在类定义体之外定义,则用于返回类型的名字在类作用域之外

 

一般名字查找
1. 在使用该名字的块中查找名字的声明,只考虑在该项使用之前声明的名字
2. 如果找不到该名字,则在包围的作用域中查找
3. 如果找不到任何声明,则程序出错,在 C++ 程序中,所有名字必须在使用之前声明

 

类定义实际上是在两个阶段中处理:
1. 首先,编译成员声明
2. 只有在所有成员出现之后,才编译它们的定义本身

 

类成员声明的名字查找
1. 检查出现在名字使用之前的类成员的声明
2. 如果第 1 步查找不成功,则检查包含类定义的作用域中出现的声明以及出现在类定义之前的声明

必须在类中先定义类型名字,才能将它们用作数据成员的类型,或者成员函数的返回类型或形参类型

一旦一个名字被用作类型名,该名字就不能被重复定义

类成员定义中的名字查找


1. 首先检查成员函数局部作用域中的声明
2. 如果在成员函数中找不到该名字的声明,则检查对所有类成员的声明
3. 如果在类中找不到该名字的声明,则检查在此成员函数定义之前的作用域中出现的声明

 

如果类的成员被屏蔽了,可以通过用类名来限定成员名或显式使用 this 指针来使用它


构造函数

 

构造函数不能是const 的,不管对象是否为 const,都用一个构造函数来初始化化该对象

 

构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式

 

从概念上讲,可以认为构造函数分两个阶段执行:
1. 初始化阶段
2. 普通的计算阶段 计算阶段由构造函数函数体中的所有语句组成

不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化,初始化发生在计算阶段开始之前

 

在构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化:运行该类型的默认构造函数,来初始化类类型的数据成员;内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为 0

 

有些成员必须在构造函数初始化列表中进行初始化,对于这样的成员,在构造函数函数体中对它们赋值不起作用

 

对非类类型的数据成员进行赋值或使用初始化式在结果和性能上都是等价的

 

以初始化 const 对象或引用类型的对象,但不能对它们赋值,在开始执行构造函数的函数体之前,要完成初始化

初始化 const 或引用类型数据成员的唯一机会是构造函数初始化列表中

 

必须对任何 const 或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式

 

构造函数初始化列表仅指定用于初始化成员的值,并不指定这些初始化执行的次序,成员被初始化的次序就是定义成员的次序

 

初始化的次序常常无关紧要,然而,如果一个成员是根据其他成员而初始化,则成员初始化的次序是至关重要的

 

按照与成员声明一致的次序编写构造函数初始化列表是个好主意,此外,尽可能避免使用成员来初始化其他成员

一般情况下,通过(重复)使用构造函数的形参而不是使用对象的数据成员,可以避免由初始化式的执行次序而引起的任何问题

 

初始化式可以是任意表达式

 

只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数

 

如果类包含内置或复合类型的成员,则该类不应该依赖于合成的默认构造函数,它应该定义自己的构造函数来初始化这些成员

 

类通常应定义一个默认构造函数

 

实际上,如果定义了其他构造函数,则提供一个默认构造函数几乎总是对的,通常,在默认构造函数中给成员提供的初始值应该指出该对象是“空”的

 

可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换

 

可以通过将构造函数声明为 explicit,来防止在需要隐式转换的上下文中使用构造函数

 

explicit 关键字只能用于类内部的构造函数声明上,在类的定义体外部所做的定义上不再重复它

 

当构造函数被声明 explicit 时,编译器将不使用它作为转换操作符

 

通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为 explicit,将构造函数设置为 explicit 可以避免错误,并且当转换有用时,用户可以显式地构造对象

 

 

#include <iostream>
#include <string>
using namespace std;

class First {
public:
    int memi;
    double memd;
};

class Second {
public:
    int memi;
    double memd;
};


typedef double Money;
class Account {
public:
    Account(){}
    //explicit Account(Money money):bal(money){}
    Account(Money money):bal(money){}
    Money balance() { return bal; } // uses global definition of Money
    bool equals(Account a) const {
        return bal - a.balance();
    }
private:
 //typedef long double Money; error: cannot change meaning of Money
    Money bal;
 // ...
};

 
// Note: This code is for illustration purposes only and reflects bad practice
// It is a bad idea to use the same name for a parameter and a member
int height;
class Screen {
public:
    void dummy_fcn_1(int height) {
         cursor = width * height; // which height? The parameter
    }
     
    // bad practice: Don't hide names that are needed from surrounding scopes
    void dummy_fcn_2(int height) {
         cursor = width * ::height;// which height? The global one
    }
    //Screen() const{} error! constructors may not be `const' 
private:
    int cursor;
    int height, width;
};


class ConstRef {
 public:
     ConstRef(int ii);
 private:
     int i;
     const int ci;
     int &ri;
};
 // no explicit constructor initializer: error ri is uninitialized
 ConstRef::ConstRef(int ii): ci(ii),ri(ii) {             
      // assignments
      i = ii;   // ok
      //ci = ii;   error: cannot assign to a const
      //ri = i;    assigns to ri which was not bound to an object
 }

class X {
     int i;
     int j;
 public:
     // run-time error: i is initialized before j
     X(int val): j(val), i(j) { 
           cout << "i = " << i << endl;      
     }
 };


int main()
{    
    First obj1;
    //Second obj2 = obj1;e rror: obj1 and obj2 have different types

    X x(2);   // i = ? 
    
    Account a = Account(); //ok: create an unnamed, empty Account use to initialize a
    Account a1(98.1);
    bool b = a1.equals(99.9); //Implicit Class-Type Conversions if explicit this will be an error
    cout << boolalpha << b << endl;

    return 0;
}

 

 

 

 

0
0
分享到:
评论

相关推荐

    浅谈C++ Explicit Constructors(显式构造函数)

    C++中的显式构造函数(Explicit Constructors)是编程实践中一个重要的概念,主要用来控制类对象的隐式类型转换。在C++中,如果一个类有一个只接受一个参数的构造函数,那么这个构造函数可以被用来进行隐式类型转换...

    -C++参考大全(第四版) (2010 年度畅销榜

    23.6 explicit构造函数 23.7 成员初始化语法 23.8 利用关键字asm 23.9 连接说明 23.10 基于数组的I/O 23.11 C与C++的区别 第24章 标准模板库 24.1 STL概述 24.2 容器类 24.3 一般的操作原理 24.4 vector容器 24.5 ...

    C++ Primer中文版(第5版)李普曼 等著 pdf 1/3

     13.6.2 移动构造函数和移动赋值运算符 473  13.6.3 右值引用和成员函数 481  小结 486  术语表 486  第14章 操作重载与类型转换 489  14.1 基本概念 490  14.2 输入和输出运算符 494  14.2.1 重载输出...

    C++Primer(第5版 )中文版(美)李普曼等著.part2.rar

     13.6.2 移动构造函数和移动赋值运算符 473  13.6.3 右值引用和成员函数 481  小结 486  术语表 486  第14章 操作重载与类型转换 489  14.1 基本概念 490  14.2 输入和输出运算符 494  14.2.1 重载输出...

    数组类的分功能实现

    `MyArray.h`文件通常包含数组类的声明,它定义了类的接口,包括构造函数、析构函数、成员函数等。在C++中,我们可能会定义一个名为`MyArray`的类,其中包含动态分配内存的数组,这样可以创建大小可变的数组。这个类...

    C++大学教程,一本适合初学者的入门教材(part2)

    21.9 explicit构造函数 21.10 mutable类成员 21.11 类成员指针(.和—&gt;) 21.12 多重继承与virtual基类 21.13 结束语 小结 术语 自测练习 自测练习答案 练习 附录A 运算符的优先级与结台律 附录B ASCII字符集 ...

    C++大学教程,一本适合初学者的入门教材(part1)

    21.9 explicit构造函数 21.10 mutable类成员 21.11 类成员指针(.和—&gt;) 21.12 多重继承与virtual基类 21.13 结束语 小结 术语 自测练习 自测练习答案 练习 附录A 运算符的优先级与结台律 附录B ASCII字符集 ...

    STL 标准模板库简介

    explicit关键字用于构造函数声明,防止隐式类型转换,避免程序员无意中创建了不必要的临时对象,提高代码的清晰性和安全性。 STL的使用极大地提高了C++程序员的生产力,它提供的工具使得复杂的数据结构和算法操作...

    软件研发面试宝典:纸上谈兵

    - 面向对象编程:理解构造函数、虚函数、静态成员函数、拷贝构造函数与赋值运算符的重载、列表初始化、多态、静态与动态绑定、explicit、mutable、volatile、const_cast等C++特性。 - 设计模式:掌握常用的设计...

    不粗的面试题

    - 面向对象编程:构造函数、虚函数、静态成员函数、拷贝构造与赋值运算符、列表初始化、多态、静态绑定与动态绑定、explicit、mutable、volatile、internal、继承、堆栈溢出、重载操作符、Final关键字、C#概述。...

    C++常用术语

    C++支持默认构造函数、带参数构造函数和拷贝构造函数。 #### 15. 析构函数(Destructor) 析构函数是在对象生命周期结束时自动调用的特殊成员函数,用于清理资源。在C++中,析构函数通常用于释放动态分配的内存和...

    C++工程师106道面试题总结.pdf

    而new是一个运算符,会先分配内存再调用构造函数,并且在分配失败时会抛出异常。Free和delete用于释放内存,free是库函数,delete是运算符。 调用约定__stdcall和__cdecl主要在函数调用时影响堆栈的清理工作。__...

    一个简单的Qt开发脚本示例,它展示了如何使用Qt Creator创建一个简单的窗口应用程序,并在该窗口中放置一个按钮

    - **解释**:这部分代码实现了 `MainWindow` 类的构造函数和析构函数,以及槽函数 `on_pushButton_clicked()`。构造函数中创建了一个按钮,并将其添加到主窗口上,同时设置了按钮的位置和大小。通过 `QObject::...

    三星笔试题

    "三星笔试题" 本资源是三星笔试题的知识点总结,涵盖了计算机系统、操作系统、数据库、编程语言、数据结构、算法等多...包括B公有继承自A、B实现了隐式转化为A的转化、A实现了non-explicit的参数为B的构造函数等情况。

    QComboBox 下拉可多选,可搜索

    首先,我们需要在构造函数中初始化组件,包括设置编辑框的父级为QComboBox,并将其与下拉列表连接起来,以便在输入时触发搜索过滤。此外,还需重写`onTextChanged`槽函数,根据输入的文本过滤下拉列表。对于多选功能...

    觅职渣记-互联网技术类笔试面试总结

    列表初始化是一种更加安全的初始化方式,可以用于构造函数、函数返回值和变量初始化。 **4. 多态** 多态性允许一个接口被不同类的对象所使用。实现多态的方式包括虚函数和抽象类。 **5. 静态绑定与动态绑定** - ...

    c++游戏程序员面试题

    1. **定义:** `explicit`关键字用于单参数构造函数,阻止隐式类型转换。 2. **应用场景:** - 避免意外的类型转换引发的错误。 **Cast** 1. **概述:** C++提供了四种显式类型转换操作符。 2. **种类及其应用:*...

Global site tag (gtag.js) - Google Analytics