1.返回引用与非引用的区别
返回引用示例:
#include <iostream>
#include <string>
namespace test_starthis {
class Screen{
public:
Screen() = default;
Screen(int x, int y, std::string contents): x_(x), y_(y), contents_(contents), ct_(0){
std::cout << "Screen ctor with parameter called." << std::endl;
}
// 类内实现,默认inline
Screen& set(std::string new_contents){
contents_ = new_contents;
return *this;
}
Screen& move(int x, int y) {
x_ = x;
y_ = y;
return *this;
}
// 从const函数返回的对象是const对象,因此返回值也需要加const
const Screen& display() const {
std::cout << "contents_: " << contents_ << std::endl;
std::cout << "x_: " << x_ << ", y_: " << y_ << std::endl;
++ct_; // 虽然函数是const,但由于ct_被mutable修饰了,因此可以修改ct_
std::cout << "The screen is displayed " << ct_ << " times." << std::endl;
return *this; // *this是const对象
}
private:
int x_ = 0;
int y_{0};;
std::string contents_{"my screen"};
mutable unsigned int ct_{0}; // 如果想在const成员函数内修改类的某个数据成员,可在该数据成员的声明中加mutable关键字
};
auto main() -> int {
Screen screen;
screen.display();
screen.set("green screen").move(8,8).display();
screen.move(6,6).set("red screen").display();
//screen.display().set("error"); // [MinGW Makefiles] error: passing 'const test_starthis::Screen' as 'this' argument discards qualifiers [-fpermissive]
return 0;
}
}
输出:
contents_: my screen
x_: 0, y_: 0
The screen is displayed 1 times.
contents_: green screen
x_: 8, y_: 8
The screen is displayed 2 times.
contents_: red screen
x_: 6, y_: 6
The screen is displayed 3 times.
上述的操作screen.set("green screen").move(8,8).display();
和screen.move(6,6).set("red screen").display();
都是在同一个对象上执行的。
如果令move
和set
返回Screen
而非Screen&
则上面两句的行为将大不相同。简单起见使用一个更简单的示例说明。
#include <iostream>
#include <string>
namespace test_starthis {
class Screen{
public:
Screen() = default;
Screen(int x, int y, std::string contents): x_(x), y_(y), contents_(contents), ct_(0){
std::cout << "Screen ctor with parameter called." << std::endl;
}
// 类内实现,默认inline
Screen set(std::string new_contents){
contents_ = new_contents;
return *this;
}
Screen move(int x, int y) {
x_ = x;
y_ = y;
return *this;
}
// 从const函数返回的对象是const对象,因此返回值也需要加const
const Screen& display() const {
std::cout << "contents_: " << contents_ << std::endl;
std::cout << "x_: " << x_ << ", y_: " << y_ << std::endl;
++ct_; // 虽然函数是const,但由于ct_被mutable修饰了,因此可以修改ct_
std::cout << "The screen is displayed " << ct_ << " times." << std::endl;
return *this; // *this是const对象
}
private:
int x_ = 0;
int y_{0};;
std::string contents_{"my screen"};
mutable unsigned int ct_{0}; // 如果想在const成员函数内修改类的某个数据成员,可在该数据成员的声明中加mutable关键字
};
auto main() -> int {
Screen screen;
screen.display();
screen.move(6,6).set("red screen").display();
screen.display();
return 0;
}
}
输出:
contents_: my screen
x_: 0, y_: 0
The screen is displayed 1 times.
contents_: red screen // 这是函数返回值副本的内容
x_: 6, y_: 6 // 这是函数返回值副本的左边
The screen is displayed 2 times. // 这是函数返回值副本的display
contents_: my screen // 从此处可以看出screen的内容并未改变
x_: 6, y_: 6
The screen is displayed 2 times.
本示例中screen.move(6,6).set("red screen").display();
相当于:
Screen tmp1 = screen.move(6,6);
Screen tmp2 = tmp1.set("red screen");
tmp2 = tmp2.display(); // 因为display还是引用,因此此处没有副本产生
2.基于const
的重载
#include <iostream>
#include <string>
namespace test_starthis {
class Screen{
public:
Screen() = default;
Screen(int x, int y, std::string contents): x_(x), y_(y), contents_(contents), ct_(0){
std::cout << "Screen ctor with parameter called." << std::endl;
}
// 类内实现,默认inline
Screen set(std::string new_contents){
contents_ = new_contents;
return *this;
}
Screen move(int x, int y) {
x_ = x;
y_ = y;
return *this;
}
// 从const函数返回的对象是const对象,因此返回值也需要加const
const Screen& display() const {
std::cout << "const version -->\n";
do_display();
return *this;
}
Screen& display() {
std::cout << "non-const version -->\n";
do_display();
return *this;
}
void do_display() const {
std::cout << "contents_: " << contents_ << std::endl;
std::cout << "x_: " << x_ << ", y_: " << y_ << std::endl;
++ct_;
std::cout << "The screen is displayed " << ct_ << " times." << std::endl;
}
private:
int x_ = 0;
int y_{0};;
std::string contents_{"my screen"};
mutable unsigned int ct_{0}; // 如果想在const成员函数内修改类的某个数据成员,可在该数据成员的声明中加mutable关键字
};
auto main() -> int {
Screen screen1;
screen1.display(); // 调用非const版本
const Screen screen2(6, 6, "red screen");
screen2.display(); // 调用const版本
return 0;
}
}
输出:
non-const version -->
contents_: my screen
x_: 0, y_: 0
The screen is displayed 1 times.
Screen ctor with parameter called.
const version -->
contents_: red screen
x_: 6, y_: 6
The screen is displayed 1 times.
3.类类型前向声明
#include <iostream>
#include <string>
namespace test_starthis {
class Controller;
class Screen{
public:
// Screen() = default; // 没有意义,因为有引用成员,必须在初始化列表中初始化
Screen(Controller &controller): controller2_(controller){ // 此处Controller &controller不可改成Controller controller
std::cout << "Screen ctor with parameter called." << std::endl;
}
void setController(Controller); // forward declaration的incomplete type可用于函数参数
Controller getController(); // 也可用于函数返回值
private:
//Controller controller_; // error: field 'controller_' has incomplete type 'test_starthis::Controller'
Controller* controller1_; // [RIGHT],可使用指针形式
Controller& controller2_; // [RIGHT],可使用引用形式,但要注意构造函数中的入参也必须是引用,因为想要创建类的对象该类必须被定义过,前向声明不是定义
};
auto main() -> int {
// 完成Controller的定义后
// Controller controller;
//Screen screen(controller);
return 0;
}
}