- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
最佳答案
介绍
C++使用值语义处理用户定义类型的变量。
这意味着对象会在各种上下文中隐式复制,
我们应该了解“复制对象”的实际含义。
让我们考虑一个简单的示例:
class person
{
std::string name;
int age;
public:
person(const std::string& name, int age) : name(name), age(age)
{
}
};
int main()
{
person a("Bjarne Stroustrup", 60);
person b(a); // What happens here?
b = a; // And here?
}
(如果您对
name(name), age(age)
部分感到困惑,
person
对象是什么意思?
main
函数显示了两种不同的复制方案。
person b(a);
由复制构造函数执行。
b = a
由副本分配运算符执行。
The [...] copy constructor and copy assignment operator, [...] and destructor are special member functions.[ Note: The implementation will implicitly declare these member functionsfor some class types when the program does not explicitly declare them.The implementation will implicitly define them if they are used. [...] end note ][n3126.pdf section 12 §1]
The implicitly-defined copy constructor for a non-union class X performs a memberwise copy of its subobjects.[n3126.pdf section 12.8 §16]
The implicitly-defined copy assignment operator for a non-union class X performs memberwise copy assignmentof its subobjects.[n3126.pdf section 12.8 §30]
person
的隐式定义的特殊成员函数如下所示:
// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}
// 2. copy assignment operator
person& operator=(const person& that)
{
name = that.name;
age = that.age;
return *this;
}
// 3. destructor
~person()
{
}
在这种情况下,按成员复制正是我们想要的:
name
和
age
,因此我们获得了一个独立的
person
对象。
person
析构函数完成之后,隐式调用成员的析构函数:
After executing the body of the destructor and destroying any automatic objects allocated within the body,a destructor for class X calls the destructors for X's direct [...] members[n3126.pdf 12.4 §6]
std::string
这样的东西,程序员爱上了指针。
person
类可能看起来像这样:
class person
{
char* name;
int age;
public:
// the constructor acquires a resource:
// in this case, dynamic memory obtained via new[]
person(const char* the_name, int the_age)
{
name = new char[strlen(the_name) + 1];
strcpy(name, the_name);
age = the_age;
}
// the destructor must release this resource via delete[]
~person()
{
delete[] name;
}
};
即使到了今天,人们仍然以这种风格编写类(class)并陷入困境:
name
成员仅复制一个指针,而不复制它指向的字符数组!
a
可以观察到b
的更改。 b
被销毁,a.name
是一个悬空的指针。 a
被破坏,则删除悬空指针将产生undefined behavior。 name
指向的内容,// 1. copy constructor
person(const person& that)
{
name = new char[strlen(that.name) + 1];
strcpy(name, that.name);
age = that.age;
}
// 2. copy assignment operator
person& operator=(const person& that)
{
if (this != &that)
{
delete[] name;
// This is a dangerous point in the flow of execution!
// We have temporarily invalidated the class invariants,
// and the next statement might throw an exception,
// leaving the object in an invalid state :(
name = new char[strlen(that.name) + 1];
strcpy(name, that.name);
age = that.age;
}
return *this;
}
注意初始化和赋值之间的区别:
name
之前拆除旧状态,以防止内存泄漏。
x = x
形式的自我分配。
delete[] name
将删除包含源字符串的数组,
x = x
时,
this->name
和
that.name
都包含相同的指针。
new char[...]
由于内存耗尽而引发异常,则该解决方案将失败。
// 2. copy assignment operator
person& operator=(const person& that)
{
char* local_name = new char[strlen(that.name) + 1];
// If the above statement throws,
// the object is still in the same state as before.
// None of the following statements will throw an exception :)
strcpy(local_name, that.name);
delete[] name;
name = local_name;
age = that.age;
return *this;
}
这也可以在没有明确检查的情况下进行自我分配。
private
而不给出定义:
private:
person(const person& that);
person& operator=(const person& that);
或者,您可以继承
boost::noncopyable
或将其声明为已删除(在C++ 11及更高版本中):
person(const person& that) = delete;
person& operator=(const person& that) = delete;
三法则
If you need to explicitly declare either the destructor,copy constructor or copy assignment operator yourself,you probably need to explicitly declare all three of them.
class person
{
std::string name;
int age;
public:
person(const std::string& name, int age); // Ctor
person(const person &) = default; // 1/5: Copy Ctor
person(person &&) noexcept = default; // 4/5: Move Ctor
person& operator=(const person &) = default; // 2/5: Copy Assignment
person& operator=(person &&) noexcept = default; // 5/5: Move Assignment
~person() noexcept = default; // 3/5: Dtor
};
零法则std::string
)已经为您完成了。std::string
成员比较简单的代码char*
进行复杂且容易出错的选择,您应该被说服。关于c++ - 什么是三法则?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46282900/
在 SO解释了为什么像 scalaz、cats (Scala) 或 Arrow (Kotlin) 中的 Validation 不能是 monad。 据我所知,这是因为他们已经根据应用仿函数对 mona
给定例如像 这样的类型 data Tree a = Branch (Tree a) (Tree a) | Leaf a 我可以轻松地为 Functor、Applicative、
从这里引用:https://en.wikipedia.org/wiki/Law_of_Demeter More formally, the Law of Demeter for functions r
以下代码打破了 Law of Demeter : public class Student extends Person { private Grades grades; public Stu
我有一个简单的 Store 类,其中包含一个 Inventory。 Inventory 包含一个 Item 列表。为了修改 Inventory 中的其中一个 Item,我必须这样写: Store st
public class BigPerformance { public decimal Value { get; set; } } public class Performance
当我需要多态行为时,我经常发现自己在 C++ 中使用唯一指针。我通常实现如下所示的纯抽象类: class A { public: virtual A* clone() const = 0; /
5 规则指出,如果一个类有一个用户声明的析构函数、复制构造函数、复制赋值构造函数、移动构造函数或移动赋值构造函数,那么它必须有其他 4 个。 但今天我突然明白了:你什么时候需要用户定义的析构函数、复制
编译器或库的更“ native ”部分(IO 或可以访问黑魔法和实现的函数)是否对这些定律做出假设?打破它们会导致不可能的事情发生吗? 或者它们只是表达了一种编程模式——也就是说,你唯一会因为破坏它们
我有点想用 Java 8 流编写 Selenium 页面对象,如下面的代码所述,并收到评论说我的代码违反了 Demeter 法则,因为我在一行中执行了很多操作。我被建议将代码分解为第一个流以收集列表并
我对如何避免一对多关联违反得墨忒耳法则感到困惑。假设我有一个这样的模型: class Organization < ActiveRecord::Base has_one :address ha
如果我有一个对象的 ArrayList,那么任何时候我需要调用 ArrayList 成员的任何方法时,我都需要这样做: list.get(i).doSomething(); 这看起来很可疑地违反了 D
我在我的 Purescript 代码中广泛使用了来自库和我自己的类型类。每个类型类法则似乎都提供了一个很好的测试。目前,我正在为每个类(class)和法律单独编写测试。有没有办法部分自动化?也许像 H
我看到提到过 ListT is a classic example of a buggy monad transformer that doesn't satisfy the monad laws.
当我审查一些代码时,我看到了这个片段。 List users = /* Some code that initializes the list */; users.stream() .fil
我是一名优秀的程序员,十分优秀!