赞
踩
基本类型 | void, bool, char, int, double, … |
简单聚合 | 主要目的:数据分组 聚合:可能包含一个或多个基本类型或其他兼容的聚合类型 无法控制组成类型的相互作用。 简单,如果只有(编译器生成)默认构造函数/析构函数/拷贝构造函数/赋值函数 标准的内存布局(所有成员按声明顺序连续排列),如果所有成员都具有相同的访问控制(例如全部是公共的) |
更复杂自定义类型 | 主要目的:确保正确性和安全性保证。 自定义不变量和对成员相互作用的控制。 限制成员访问。 成员函数 用户定义的构造函数/成员初始化。 用户自定义析构函数/拷贝构造函数/赋值函数。 可以是多态的(包含虚成员函数)。 |
正确性保证
可复用的抽象
资源管理
也被称为 RAII(资源获取即初始化)
monotonous_counter c;
cout << c.reading(); // prints 0
c.increment();
cout << c.reading(); // prints 1
c.increment();
c.increment();
cout << c.reading(); // prints 3
简单的聚合类型不能保证:
struct frail_counter {
int count;
};
frail_counter c;
cout << c.count; // any value
c.count++;
c.count = 11;
struct chaotic_sequence {
std::vector<int> nums;
};
chaotic_sequence s;
s.nums.push_back(8); 8
s.nums.push_back(1); 8 1
s.nums.push_back(4); 8 1 4
s.nums.pop_back(4); 8 1
可能违反要求
class monotonous_counter {
int count_; // ← 数据成员
…
void increment () { // ← 成员函数
++count_;
}
};
class ascending_sequence {
std::vector<int> seq_; // ← 数据成员
…
void insert (int x) { // ← 成员变量
// 在正确的位置将 x 插入到 nums 中
}
};
成员函数可用于
私有成员只能通过成员函数访问:
class ascending_sequence { private: std::vector<int> seq_; // … more private members public: void insert (int x) { … } auto size () const { return seq_.size(); } // … more public members }; int main () { ascending_sequence s; s.insert(8); // 'insert' 是公共的 auto n = s.size(); // 'size' 是公共的 auto i = s.seq_[0]; // 编译错误: 'seq_' 是私有的 auto m = s.seq_.size(); // 编译错误 s.seq_.push_back(1); // 编译错误 }
struct 与 class – 主要区别在于默认可见性:
关键字 | 通常用于 |
---|---|
struct | 公共数据的简单聚合 |
class | 私有数据、成员函数、不变量…… |
只有带 const 修饰的成员函数才能被 const 对象调用:
class ascending_sequence {
std::vector<int> seq_;
public: …
void insert { … }
auto size () const { return seq_.size(); }
};
int main () {
ascending_sequence s;
s.insert(88); // s不是const的
auto const& cs = s;
cs.insert(5); // 编译错误: 'insert' 不是const的
}
接受常量(引用)参数的函数不仅承诺不修改它,这个承诺还将被编译器检查并强制执行。
void foo (ascending_sequence const& s) {
// 's' is const reference ^^^^^
auto n = s.size(); // 'size' 是 const
s.insert(5); // 编译错误: 'insert' 不是 const
}
const成员函数内部的成员是const
class monotonous_counter {
int count_;
public: …
int reading () const {
// 编译错误: count_ 是 const:
count_ += 2;
return count_;
}
};
class ascending_sequence {
std::vector<int> seq_;
public: …
auto size () const { // 'seq_' 是 const
// 编译错误: 调用非const的'push_back'
seq_.push_back(0);
// vector的成员 'size()' 是const的
return seq_.size();
}
};
成员函数可以通过const进行重载
如果一个成员函数是const-限定的,另一个不是,它们可以有相同的名称(和参数列表)。这样可以清楚地区分只读访问和读写操作。
class interpolation { …
int t_;
…
public:
…
// 读/写函数对:
void threshold (int t) { if (t > 0) t_ = t; }
int threshold () const { return t_; }
// 可写访问一个'node'
node& at (int x) { … }
// 只读访问一个'node'
node const& at (int x) const { … }
};
class MyType {
int n_;
// 更多的成员 …
public:
// 声明 + 内联定义
int count () const { return n_; }
// 只声明
double foo (int, int);
};
// 独立定义
double MyType::foo (int x, int y) {
// lots of stuff …
}
特殊成员函数
class X { …
Y operator [] (int i) { … }
};
使用下标运算符。
X x;
Y y = x[0];
1.成员初始化器 (C++11)
class counter {
// counter 应该从0开始
int count_ = 0;
public:
…
};
class Foo {
int i_ = 10;
double x_ = 3.14;
public:
…
};
2.构造函数初始化列表
构造函数(ctor) = 创建对象时执行的特殊成员函数
class counter {
int count_;
public:
counter(): count_{0} { }
…
};
class Foo {
int i_; // 1st
double x_; // 2nd
public:
Foo(): i_{10}, x_{3.14} { }
// same order: i_ , x_
…
};
提示:确保初始化列表中的成员顺序始终是与成员声明顺序相同!
构造函数(ctor) = 创建对象时执行的特殊成员函数
class MyType { …
public:
MyType (); // 声明
…
};
// 独立定义
MyType::MyType (): … { … }
注意:确保初始化列表中的成员顺序始终是 与成员声明顺序相同!
没有用户定义的构造函数⇒编译器生成一个
class BoringType { public: int i = 0; };
BoringType obj1; // 正确
BoringType obj2 {}; // 正确
至少有一个特殊构造函数
⇒ 编译器不生成默认构造函数
class SomeType { …
public:
// special constructor:
explicit SomeType (int x) … { … }
};
SomeType s1 {1}; // 特殊 (int) 构造函数
SomeType s2; // 编译错误: 没有默认构造函数!
SomeType s3 {}; // 编译错误: 没有默认构造函数!
TypeName() = default;
⇒ 编译器生成默认构造函数的实现(编译器实现没有参数的构造函数就是默认构造函数)
// 函数有一个 'Counter' 参数
void foo (Counter c) { … }
void bar (Counter const& c) { … }
隐式转换(不好的方式)
class Counter {
int count_ = 0;
public:
Counter (int initial):
count_{initial} {}
…
};
// 从‘2‘创建了'Counter'对象
foo(2); // 正确
bar(2); // 正确
foo(Counter{2}); // 正确
bar(Counter{2}); // 正确
显式构造函数(推荐的方式)
class Counter {
int count_ = 0;
public:
explicit
Counter (int initial):
count_{initial} {}
…
};
// 没有隐式转换:
foo(2); // 编译错误
bar(2); // 编译错误
foo(Counter{2}); // 正确
bar(Counter{2}); // 正确
注意:默认情况下,让用户定义的构造函数显式!
= 调用初始化列表中的其他构造函数
class Range { int a_; int b_; public: // 1) 特殊构造函数 explicit Range (int a, int b): a_{a}, b_{b} { if (b_ > a_) std::swap(a_,b_); } // 2) 特殊[a,a]构造 - 委托给[a,b]构造函数 explicit Range (int a): Range{a,a} {} // 3) default constructor - delegates to [a,a] ctor Range (): Range{0} {} … }; Range r1; // 3) ⇒ r1.a_: 0 r1.b_: 0 Range r2 {3}; // 2) ⇒ r2.a_: 3 r2.b_: 3 Range r3 {4,9}; // 1) ⇒ r3.a_: 4 r3.b_: 9 Range r4 {8,2}; // 1) ⇒ r4.a_: 2 r4.b_: 8
由于C++语法中的歧义,无法使用空括号进行对象构造:
class A { … };
A a (); // 声明了没有参数和返回值的函数'a'
A a; // 构造一个A类型对象
A a {}; // 构造一个A类型对象
每种类型都应该有一个目的
保持数据成员私有并使用成员函数访问/修改数据
const - 限定所有非修改成员函数
接口应该易于正确使用,并且难以错误使用。
— Scott Meyers
函数或类型的用户不应该对其目的、参数的意义、先决条件/后置条件和副作用感到困惑。
#include <cstdint>
#include <numeric_limits>
class monotonous_counter {
public:
// 公共类型别名
using value_type = std::uint64_t;
private:
value_type count_ = 0;
public:
value_type reading () const { return count_; }
…
};
const auto max = std::numeric_limits<monotonous_counter::value_type>::max();
不要泄露实现细节:
如何实现一个特性/添加新功能?
示例:间隔类型 gap类
如何实现一个函数,使新的间隔对象的两个边界都移动相同的量?
class gap {
int a_;
int b_;
public:
explicit gap (int a, int b): a_{a}, b_{b} {}
int a () const { return a_; }
int b () const { return b_; }
};
推荐的独立式函数实现
gap shifted (gap const& g, int x) {
return gap{g.a()+x, g.b()+x};
}
不推荐的成员函数实现
class gap {
…
gap shifted (int x) const {
return gap{a_+x, b_+x};
}
};
推荐的描述性操作:
class Account { …
void deposit (Money const&);
Money try_withdraw (Money const&);
Money const& balance () const;
};
不推荐的Setter/Getter对:
class Account { …
void set_balance (Money const&);
Money const& balance () const;
};
名称应反映类型/函数的用途
推荐的:可理解的
class IPv6_Address {…};
class ThreadPool {…};
class cuboid {…};
double volume (cuboid const&) {…}
不推荐的:太笼统了
class Manager {…};
class Starter {…};
class Pool {…};
int get_number (Pool const&) {…}
不要在类型、变量、函数、私有数据成员等名称中使用前导下划线或双下划线!
⇒编译器作为正确性检查器,如果它能编译通过,它应该是正确的
// 明确的接口:
double volume (Cuboid const&);
// 输入保证:角度以弧度为单位
Square make_rotated (Square const&, Radians angle);
// 只接受有效数量(例如:> 0)
Gadget duplicate (Gadget const& original, Quantity times);
// 结果保证:向量已被规范化。
UnitVector3d dominant_direction (WindField const&);
//避免混淆,使用一个好的单位库。
si::kg mass (EllipsoidShell const&, si::g_cm3 density);
bool has_cycles (DirectedGraph const&);
// 易于理解的控制流程和逻辑:
Taxon species1 = classify(image1);
Taxon species2 = classify(image2);
Taxon lca = taxonomy.lowest_common_ancestor(species1, species2);
#include <iostream> // std::cout #include <cstdint> // std::uint64_t class monotonous_counter { public: using value_type = std::uint64_t; private: value_type count_ = 0; // initial public: monotonous_counter () = default; explicit monotonous_counter (value_type init) noexcept: count_{init} {} void increment () noexcept { ++count_; } [[nodiscard]] value_type reading () const noexcept { return count_; } }; int main () { monotonous_counter c; c.increment(); std::cout << c.reading(); // prints 1 c.increment(); c.increment(); std::cout << c.reading(); // prints 3 }
‘insert’ 操作的实现以及 ‘begin’ 和 ‘end’ 成员函数的作用在我们学习了迭代器和标准库中的算法后会变得更加清晰。
#include <iostream> // std::cout #include <vector> // std::vector #include <algorithm> // std::lower_bound class ascending_sequence { public: using value_type = int; private: using storage_t = std::vector<value_type>; storage_t seq_; public: using size_type = storage_t::size_type; void insert (value_type x) { // use binary search to find insert position seq_.insert(std::lower_bound(seq_.begin(), seq_.end(), x), x); } [[nodiscard]] value_type operator [] (size_type idx) const noexcept { return seq_[idx]; } [[nodiscard]] size_type size () const noexcept { return seq_.size(); } // enable range based iteration [[nodiscard]] auto begin () const noexcept { return seq_.begin(); } [[nodiscard]] auto end () const noexcept { return seq_.end(); } }; int main () { ascending_sequence s; // s.seq_: s.insert(7); // s.seq_: 7 s.insert(2); // s.seq_: 27 s.insert(4); // s.seq_: 247 s.insert(9); // s.seq_: 2479 s.insert(5); // s.seq_: 24579 std::cout << s[3]; // prints 7 for (auto x : s) { std::cout << x <<' '; // 2 4 5 7 9 } // use type aliases ascending_sequence::value_type x = 1; ascending_sequence::size_type n = 2; }
附上原文地址
如果文章对您有用,请随手点个赞,谢谢!^_^
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。