1.左值与右值
In concept (though not always in practice), rvalues correspond to temporary objects returned from functions, while lvalues correspond to objects you can refer to, either by name or by following a pointer or lvalue reference.
确定表达式是左值还是右值的方式是看是否可以获取到表达式的地址,如果可以就是左值,否则就是右值。表达式是左值还是右值跟表达式本身的类型是独立的。
例如,当一个parameter是右值引用的时候parameter本身是左值。
class Widget {
public:
Widget(Widget&& rhs); // rhs是左值,虽然它的类型是右值引用
// ...
};
在上述代码中,在移动构造函数Widget(Widget&& rhs);
内部是可以获取rhs
的地址的,因此rhs
是左值,即使它的类型是右值引用。基于类似的原因,所有的parameter都是左值。【注:本文中形参和实参分别用parameter和argument表示。parameter一定是左值,argument可以是左值或右值】。
2.声明和定义
声明declaration是告诉编译器某个东西的名称和类型(type),但略去细节。
定义definition的任务是提供编译器一些声明中所遗漏的细节。
![ellipse](https://i-blog.csdnimg.cn/blog_migrate/7b7db4d58ad1a4241699bc5287885f03.png)
3.尽量以const
,enum
,inline
替换#define
- 对于单纯常量,最好以
const
对象或enums
替换#define
- 对于形似函数的宏(macros),最好改用
inline
替换#define
// 以a和b的较大值调用f函数
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a):(b))
应当改成如下形式:
template<typename T>
inline void callWithMax(const T& a, const T& b) // 由于不知道T是什么,因此采用pass by reference-to-const
{
f(a>b?a:b);
}
4.typename
后加{}
产生临时对象
test_new.h
#ifndef TEST_NEW_H_
#define TEST_NEW_H_
#include <iostream>
namespace test_new
{
extern const int bufSize;
struct Source_reference
{
char const* filename;
int line_number;
};
/* typename后加{}产生临时对象(一般会调用构造函数),但不调用构造函数 */
#define SOURCE_REF Source_reference{__FILE__, __LINE__}
auto main() -> int;
}
#endif
test_new.cpp
#include "test_new.h"
#include <iostream>
#include <vector>
namespace test_new
{
const int bufSize = 456;
auto main() -> int
{
std::cout << "testing test_new......" << std::endl;
auto sth = SOURCE_REF;
std::cout << sth.filename << ", " << sth.line_number << std::endl;
Source_reference sr = Source_reference{__FILE__, __LINE__};
std::cout << sr.filename << ", " << sr.line_number << std::endl;
std::cout << "test_new pass" << std::endl;
std::cout << "------------------------------" << std::endl;
return 0;
}
}
main.cpp
#include "test_new.h"
#include <string>
#include <iostream>
int main()
{
std::cout << "__cplusplus: " << __cplusplus << std::endl;
test_new::main();
std::cout << "test_new::bufSize = " << test_new::bufSize << std::endl;
std::cout << "The end." << std::endl;
}
输出:
__cplusplus: 199711
testing test_new......
D:\00.testcodes\code\test_new.cpp, 19
D:\00.testcodes\code\test_new.cpp, 22
test_new pass
------------------------------
test_new::bufSize = 456
The end.
5.new typename
与new typename()
的区别
new typename
产生的对象其值(或其成员的值)是未初始化的;而new typename()
产生的对象其值(或其成员的值)是初始化为0(或NULL
、nullptr
)的。不管是结构体还是基础类型都是如此。
int *pa = new int; // *pa的值可能是-842150451
int *pb = new int(); // *pb的值是0
6. POD( Plain Old Data)
简单理解能进行memcpy的类型(基础类型、结构体、类)就是POD类型。
#include <iostream>
#include <type_traits>
namespace test_pod
{
struct A{private: int i;};
class B: public A{};
struct C : private B {void print(){std::cout << "struct C" << std::endl;}};
struct D : protected C {};
// 有构造函数,析构函数就不是POD
struct E : D {~E(){std::cout << "E's dtor called." << std::endl;}};
// 成员有初值也不是POD
struct F{int i = 0;};
auto main() -> int {
std::cout << "testing test pod......\n" << std::endl;
std::cout << std::boolalpha;
std::cout << "int: " << std::is_pod<int>::value << std::endl;
std::cout << "A: " << std::is_pod<A>() << std::endl;
std::cout << "B: " << std::is_pod<B>() << std::endl;
std::cout << "C: " << std::is_pod<C>() << std::endl;
std::cout << "D: " << std::is_pod<D>::value << std::endl;
std::cout << "E: " << std::is_pod<E>::value << std::endl;
std::cout << "F: " << std::is_pod<F>::value << std::endl;
std::cout << "------------------------------" << std::endl;
return 0;
}
}
输出:
testing test pod......
int: true
A: true
B: true
C: true
D: true
E: false
F: false
------------------------------
Reference
[1]. Scott Meyers. Effective Modern C++. O’Reilly, November 2014: First Edition.