1, 当对象作为参数直接传给函数时(passing object by value),作为该对象的一个拷贝,一个新的对象会产生。
但是需要注意的是:
该新的对象被自动创建的时候, 构造函数(constructor)并不被调用,而是 复制构造函数(copy constructor)被调用;因为构造函数是初始化一个对象,而作为传入函数的参数,是现有对象的一个拷贝。
当函数结束的时候,为了参数而新创建的对象的生命周期也会结束,因此,这个时候,对象的 析构函数(destructor)会被调用。
2, 当函数返回一个对象的时候,需要注意的是, 如果该返回值不被明确地赋值给一个对象的话,一个临时的对象会被自动创建,用于其他可能的计算;同样的,对象的返回 (无论是否临时对象),对象的 复制构造函数会被调用,而不是构造函数;当临时对象完成使命(返回 has been returned)后,对象的 析构函数(destructor)会被调用。
因此,对象作为参数传给函数,以及函数返回对象的时候,特别需要注意对象的成员中有指针的情况。
● 关于复制构造函数(copy constructor)
当一个变量通过一个现有的对象被创建的时候,复制构造函数会被调用。
存在于下面三种情况(注意,赋值操作的时候,并不会被调用):
1,声明一个变量的时候,同时初始化该变量
---------------------------------------------------------------------------------
Person q("Mickey"); // 构造函数被调用
Person r(p); // 复制构造函数被调用
Person p = q; // 复制构造函数被调用
p = q; // 这是一个赋值操作, 构造函数和 复制构造函数都 不被调用
---------------------------------------------------------------------------------
2, 当对象作为参数直接传给函数时(passing object by value)
3, 当函数返回一个对象的时候
参考: http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
● 关于 运算符重载(operator overloading)
运算符重载可以通过 成员函数(member functions)和 友好函数(friend functions)实现。
但是有以下的限制:
1, 不能改变运算符的优先级
2, 不能创建新的运算符
3, 不能重载 :: sizeof ?: . **
(Thinking in C++: . .* **)
4, 重载+并不意味着同时重载了+=,其他-=等也是同样
5, 重载 =,[],-> ,() 运算符的时候,只能通过成员函数实现, 从而强制使得左操作数类型是所定义的类。
6, ++ 和 -- 的重载需要特殊的处理 (a++/a--和++a/--a的区别)
7, = 的重载需要特殊的考虑 (自我判断,指针的释放和分配)
Thinking in C++:
Murray 对于操作符重载是否使用成员函数有如下建议:
操作符 | 建议 |
所有的一元符号 | 成员函数 |
= ( ) [ ] –> –>* | 必须是成员函数 |
+= –= /= *= ^= &= |= %= >>= <<= | 成员函数 |
所有的二元符号 | 非成员函数 |
● 关于函数隐藏
子类可以通过改变返回值或者参数来隐藏父类的函数,但如果该函数是虚拟函数的话,则只能通过改变参数而不能通过改变返回值来隐藏该函数,因为相同参数的虚拟函数只能覆盖而不能隐藏,而覆盖只能返回相同的返回值类型(或者该类型的派生类)。
● 关于继承
1,构造函数,复制构造函数,析构函数,赋值(=)操作符号函数并不会自动被继承,而是被综合(synthesized)
2,如果子类明确定义了复制构造函数/赋值操作符号函数,必须明确调用父类的相应的复制构造函数/赋值操作符号函数,否则父类的缺省构造函数会被调用。
3,赋值操作符号函数只综合相同类型对象的赋值。
From <<Thinking in C++>>
----------------------------------------------------------------------------------------------
//:! :CopyRight.txt(c) 1995-2004 MindView, Inc. All rights reserved.
//: C14:SynthesizedFunctions.cpp // Functions that are synthesized by the compiler #include <iostream> using namespace std; class GameBoard { public: GameBoard() { cout << "GameBoard()/n"; } GameBoard(const GameBoard&) { cout << "GameBoard(const GameBoard&)/n"; } GameBoard& operator=(const GameBoard&) { cout << "GameBoard::operator=()/n"; return *this; } ~GameBoard() { cout << "~GameBoard()/n"; } }; class Game { GameBoard gb; // Composition public: // Default GameBoard constructor called: Game() { cout << "Game()/n"; } // You must explicitly call the GameBoard // copy-constructor or the default constructor // is automatically called instead: Game(const Game& g) : gb(g.gb) { cout << "Game(const Game&)/n"; } Game(int) { cout << "Game(int)/n"; } Game& operator=(const Game& g) { // You must explicitly call the GameBoard // assignment operator or no assignment at // all happens for gb! gb = g.gb; cout << "Game::operator=()/n"; return *this; } class Other {}; // Nested class // Automatic type conversion: operator Other() const { cout << "Game::operator Other()/n"; return Other(); } ~Game() { cout << "~Game()/n"; } }; class Chess : public Game {}; void f(Game::Other) {} class Checkers : public Game { public: // Default base-class constructor called: Checkers() { cout << "Checkers()/n"; } // You must explicitly call the base-class // copy constructor or the default constructor // will be automatically called instead: Checkers(const Checkers& c) : Game(c) { cout << "Checkers(const Checkers& c)/n"; } Checkers& operator=(const Checkers& c) { // You must explicitly call the base-class // version of operator=() or no base-class // assignment will happen: Game::operator=(c); cout << "Checkers::operator=()/n"; return *this; } }; int main() { Chess d1; // Default constructor Chess d2(d1); // Copy-constructor //! Chess d3(1); // Error: no int constructor d1 = d2; // Operator= synthesized f(d1); // Type-conversion IS inherited Game::Other go; //! d1 = go; // Operator= not synthesized // for differing types Checkers c1, c2(c1); c1 = c2; } ///:~
--------------------------------------------------------------
GameBoard()
Game()
GameBoard(const GameBoard&)
Game(const Game&)
GameBoard::operator=()
Game::operator=()
Game::operator Other()
GameBoard()
Game()
Checkers()
GameBoard(const GameBoard&)
Game(const Game&)
Checkers(const Checkers& c)
GameBoard::operator=()
Game::operator=()
Checkers::operator=()
~Game()
~GameBoard()
~Game()
~GameBoard()
~Game()
~GameBoard()
~Game()
~GameBoard()
--------------------------------------------------------------
● 关于异常
1,当一个异常在所有的层次都不被捕获的时候,terminate()会被调用
2,当一个局部变量的析构函数中抛出异常的时候,terminate()会被调用;当一个全局变量或者static变量的构造函数或者析构函数种抛出异常的时候,terminate()会被调用。
3,当一个对象在构造函数中抛出异常的时候,它的析构函数不会被调用。
参考资料:
http://www.fredosaurus.com/notes-cpp/
<<Thinking in C++>> 2ed 2000 by Bruce Eckel
Jady Leung 2004年9月30日创建
2005年2月28日更新,增加<<Thinking in C++>>内容