`
rollstone
  • 浏览: 39013 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

关于构造函数,析构函数,explicit等内容

c++ 
阅读更多
一.编译器会为我们做什么
   当声明一个空类的时候,编译器会根据需要生成:默认构造函数,拷贝(copy)构造函数,赋值操作符重载函数(copy assignment),析构函数.上面四个函数,当你声明一个类的时候,你没有定义的时候,编译器会根据需要帮你生成,他们都是 public 的并且是 inline .注意:只有这些函数被调用的时候,编译器才会主动帮你生成.对于copy assignment编译器还会检查生成的代码是否合法,如果不合法,编译器拒绝生成.
    对于copy构造函数和copy assignment操作符,编译器创建的版本只是单纯的将来源对象的每一个 non-static成员变量拷贝到目标对象.这在很多情况下会引起错误.
例如:
template<class T>
class NamedObject {
public:
NamedObject(std::string& name, const T& value);
 ...          // 假设没有声明任何 operator=
private:
  std::string& nameValue;      // 现在是一个引用
  const T objectValue;          // 现在为 const 的
};

当如下使用的时候:
std::string newDog("Persephone");
std::string oldDog("Satch");
NamedObject<int> p(newDog, 2); 
NamedObject<int> s(oldDog, 36);
p = s;     // 对于 p 中的数据成员将会发生     // 什么呢?

注意p=s这句.它是什么含义呢?
1.p.nameValue应该指向s.nameValue所指向的那个String么?这显然是不符合C++语法规则的.C++不允许"引用改指不同的对象",引用一旦赋值就不能更改.
2.p.nameValue所指向的那个String对象应该改变么?如果改变那么会影响"持有pointer或者reference而且指向该String"的其他对象.
因为有很大的不确定性和影响,编译器将拒绝编译p=s这句话,而将决定权交给你.
你如果需要在一个内含有reference成员的类里支持赋值操作(assignment),你必须自己定义赋值操作符(copy assignmen).面对内含"const 成员"的类,编译器的反应也是一样的.因为更改const成员是不合法的,编译器无法面对给const成员赋值的情况.
还有种情况:当基类(base classes)把赋值操作符(copy assignment)定义为private,编译器将拒绝为子类(derived classes)生成默认的copy assignment.因为编译器为子类创建的copy assignment会调用 base classes的 copy assignment(无访问权限).

如果一个类不想被拷贝,即不支持copy构造函数,copy assinment.有两种方法:
1.自己声明一个对应的private方法,而不去实现它(防止被成员函数或者友元函数调用).这样谁调用它,将会引发一个link错误.
2.定义个类,copy构造函数和copy assignment定义为private.不想实现这一功能的类继承这个类.因为编译器生成的copy构造函数和copy assignment函数会去调用父类的对应函数,因权限问题,会引发编译错误.这样把link时期的错误转移到编译期间.

二.关于在构造函数,析构函数和虚函数
1.多态的基类的析构函数必须为虚函数.
对工厂模式来说,工厂返回base class的指针,来指向子类对象.如果这个对象位于heap(对工厂模式来说,肯定是).我们要正确的delete掉它.
delete baseclasspointer;
如果baseclass的析构函数不是虚函数,这将会引发错误.因为它会调用base class的析构函数,子类对象部分将不会被释放!一个类被释放一部分,情况是不确定的!

如果一个类有虚函数,其每个对象将会维持一个虚函数表,这将导致对象体积的增大.会引起兼容,移植性的问题.
所以一个心得是:如果有一个virtual函数,才将其析构函数定义为virtual.

2.不要在构造函数和析构函数中使用虚函数!
对于构造函数:会先调用base class的的构造函数,此时对象是base class类型的,而不是derived  class类型.base class 构造期间,虚函数不会下降到到derived class.这样你所期待的多态就落空了.
对于析构函数:会先析构derived class的成员,这样derived class的成员会呈现未定义的状态,C++视他们为不存在.进入base class的析构函数后,对象成为base class类型对象,而C++的虚函数,dynamic_casts(运行期类型信息)也这么看待它.

三.explicit 构造函数
这个主要是防止隐式转换所引起的错误.
隐式转换只会发生在构造函数只有一个参数的情况下.所以两个以上参数就不需要这个关键字.
隐式转换的例子:
class string
{
//...
public:
/**explicit*/ string(int size); // block implicit conversion
string(const char *); //implicit conversion
~string();
};
int main()
{
string s = "hello"; //OK, convert a C-string into a string object
int ns = 0;
s = 1;
}


上面的代码中
string s = "hello";

隐式转换为:
string temp("hello");
string s = temp;

s=1

这种代码,可能是你手贱将ns写成s,这也将编译通过.
还有种情况:
string s = "A"; //string(const char *)
string s1= 'A';//调用string(int size);
所以必须小心隐式转换!!
为什么不让构造函数默认为explicit呢?
很多旧的C++代码依赖隐式转换来实现.所以默认就是隐式转换.

一,二的内容来自于 Effective C++
三的内容来自于:ANSI/ISO C++ Professional Programmer's Handbook 和网络

ANSI/ISO C++ Professional Programmer's Handbook下载地址:
http://ishare.iask.sina.com.cn/f/20517525.html
分享到:
评论

相关推荐

    没有可用的复制构造函数或复制构造函数声明

    // 不忘析构函数以释放内存 ~CArray() { delete[] data; } private: int *data; // 假设CArray持有一个int数组 size_t size; // 数组大小 }; ``` 上面的代码示例展示了如何为`CArray`类编写复制构造函数和...

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

    如果自己没有申明,编译器会为我们提供一个copy构造函数、一个copy assignment操作符和一个析构函数。此外,如果没有申明任何构造函数,编译器会为我们申明一个default构造函数。很像下面的Empty类: class Empty{ ...

    Visual C++ 2005入门经典中文版--第08章 深入理解类

    在深入探讨Visual C++ 2005关于类的高级特性之前,让我们首先回顾并深化对析构函数的理解。在C++中,析构函数是一个特殊类型的成员函数,它的主要职责是在对象生命周期结束时清理资源,例如释放动态分配的内存。 ##...

    程序设计实习(田永鸿)清华大学

    当 `COut` 对象被销毁时,析构函数的调用顺序则相反:先调用 `COut` 的析构函数,然后是 `CIn2` 和 `CIn1` 的析构函数。 通过以上分析,我们可以了解到关于类、对象以及运算符重载的基本概念及其在实际编程中的应用...

    C++类与继承.docx

    总的来说,C++的类、继承、构造函数、析构函数、友元以及运算符重载等机制为编写复杂的面向对象程序提供了基础。理解并熟练运用这些概念是成为一名优秀的C++程序员的关键。在实际编程中,合理设计类的结构,利用这些...

    《C++ primer plus》第13章 类继承1

    - **动态内存**:派生类使用`new`时,需要自定义析构函数、复制构造函数和赋值运算符,以处理基类的动态内存。 - **友元函数**:若需访问基类成员,友元可能需要显式类型转换。 7. **类设计原则** - **默认构造...

    Cpp面经200问.pdf

    构造函数、析构函数可以声明为内联函数,但虚函数通常不声明为内联,因为虚函数需要通过虚表机制实现。 #### 41. auto、decltype和decltype(auto)用法 auto用于自动类型推导,根据变量初始化表达式自动确定变量类型...

    C++思维导图Xmind文件和.png文件(持续更新)

    构造函数与析构函数思维导图Xmind文件和.png文件 拷贝构造思维导图xmind文件和.png文件 运算符重载思维导图xmind文件和.png文件 初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类思维...

    C++箴言:资源管理类的拷贝行为

    这类数据结构通过其构造函数获取资源,并在析构函数中自动释放资源,从而有效避免了资源泄漏的问题。下面以一个简单的例子来说明如何设计一个用于管理互斥量的资源管理类`Lock`。 ```cpp #include class Mutex { ...

    C++编程规范1

    - 基类的析构函数应为`virtual`,确保在多态删除对象时正确调用子类的析构函数。 - 重写基类的所有重载函数,以避免二义性问题。 - 避免不必要的`virtual`函数,除非它们在子类中是需要的。 - 如果基类的某个...

    C++_Interview_Questions

    17. **禁用默认函数**:通过声明为`=delete`可以禁用默认构造函数、默认析构函数等。 18. **拷贝构造函数**:拷贝构造函数用于创建一个新对象作为现有对象的副本。自定义拷贝构造函数用于处理特殊资源管理或深拷贝...

    面向对象程序设计模拟考题.doc

    在本模拟考题中,涉及的知识点主要涵盖C++语言中的面向对象特性,包括泛型编程、函数重载、封装、访问控制、构造与析构函数、继承、动态内存管理、STL(标准模板库)以及输入输出流等核心概念。 1. 泛型编程:C++...

    推选文档程序设计实习第十五讲-运算符重载PPT.ppt

    本讲内容主要围绕运算符重载展开,同时也涉及了类和对象的基础知识,包括类的定义、构造函数、析构函数、特殊成员函数、const成员、友元、this指针以及静态成员的使用。 首先,类和对象是面向对象编程的基础。类...

    认识 C++ 中的explicit 关键字

    // temp 的析构函数被激活 ``` 虽然这种行为在某些情况下是期望的,但有时我们并不希望这样的隐式转换发生。比如在`String`类的例子中: ```cpp class String { int size; char *p; public: explicit String...

    简答题背诵资料.doc

    - 如果类设计者没有定义任何成员函数,C++ 编译器会自动提供默认构造函数、默认拷贝构造函数、默认析构函数(如果适用)以及默认赋值操作符。 24. **类型转换**: - 类型转换可以通过显式类型转换(如 `static_...

    C++与操作系统等面试题61

    // 析构函数 ~SharedPtr() { --(*m_count_); if (*m_count_ == 0) { delete m_ptr_; delete m_count_; m_ptr_ = nullptr; m_count_ = nullptr; } } // 拷贝构造函数 SharedPtr(const SharedPtr& ptr) :...

    金山kingsoft笔试题2010 c++

    虚析构函数在C++中用于处理多态性,确保基类指针或引用删除派生类对象时能正确调用派生类的析构函数。未声明为虚的析构函数将仅调用基类的析构函数,可能会导致资源泄露。 4. **代码改错** 在给定的代码中,`...

    C++类与继承.pdf

    析构函数与构造函数相反,它在对象生命周期结束时被调用,用于执行清理工作,比如释放动态分配的内存。每个成员函数都有一个指向调用对象的this指针,通过`*this`可以引用整个对象。如果希望在const成员函数中防止...

    数组类的分功能实现

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

Global site tag (gtag.js) - Google Analytics