原来C++类中,有6个默认成员函数:
- 构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值重载
- 取地址重载
- const取地址重载
最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
以下代码在vs2013中不能体现,在vs2019下才能演示体现上面的特性。
namespace phw {
class string {
public:
typedef char *iterator;
iterator begin() {
return _str;
}
iterator end() {
return _str + _size;
}
string(const char *str = "")
: _size(strlen(str)), _capacity(_size) {
//cout << "string(char* str)" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// s1.swap(s2)
void swap(string &s) {
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 拷贝构造
string(const string &s)
: _str(nullptr) {
cout << "string(const string& s) -- 深拷贝" << endl;
string tmp(s._str);
swap(tmp);
}
// 移动构造
string(string &&s)
: _str(nullptr) {
cout << "string(string&& s) -- 移动拷贝" << endl;
swap(s);
}
// 赋值重载
string &operator=(const string &s) {
cout << "string& operator=(string s) -- 深拷贝" << endl;
string tmp(s);
swap(tmp);
return *this;
}
// s1 = 将亡值
string &operator=(string &&s) {
cout << "string& operator=(string&& s) -- 移动赋值" << endl;
swap(s);
return *this;
}
~string() {
//cout << "~string()" << endl;
delete[] _str;
_str = nullptr;
}
char &operator[](size_t pos) {
return _str[pos];
}
void reserve(size_t n) {
if (n > _capacity) {
char *tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch) {
if (_size >= _capacity) {
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
//string operator+=(char ch)
string &operator+=(char ch) {
push_back(ch);
return *this;
}
string operator+(char ch) {
string tmp(*this);
tmp += ch;
return tmp;
}
const char *c_str() const {
return _str;
}
private:
char *_str;
size_t _size;
size_t _capacity;// 不包含最后做标识的\0
};
//const bit::string& to_string(int value)
phw::string to_string(int value) {
bool flag = true;
if (value < 0) {
flag = false;
value = 0 - value;
}
phw::string str;
while (value > 0) {
int x = value % 10;
value /= 10;
str += ('0' + x);
}
if (flag == false) {
str += '-';
}
std::reverse(str.begin(), str.end());
return str;
}
}// namespace phw
// 以下代码在vs2013中不能体现,在vs2019下才能演示体现上面的特性。
class Person {
public:
Person(const char *name = "", int age = 0)
: _name(name), _age(age) {}
Person(const Person &p)
: _name(p._name), _age(p._age) {}
Person &operator=(const Person &p) {
if (this != &p) {
_name = p._name;
_age = p._age;
}
return *this;
}
// 强制生成移动构造和移动赋值
Person(Person &&p) = default;
Person &operator=(Person &&p) = default;
~Person() {
cout << "~Person()" << endl;
}
private:
phw::string _name;// 自定义类型
int _age = 1; // 内置类型
};
int main() {
Person s1("张三", 18);
Person s2 = s1;
Person s3 = std::move(s1);
cout << endl;
return 0;
}
delete
关键字用于显式地禁用某些特殊成员函数或运算符,以阻止它们在特定的上下文中被调用或使用。
使用delete
关键字,可以在类的声明中删除以下函数:
删除默认构造函数:
MyClass() = delete;
这将禁用默认构造函数,阻止对象的无参创建。
删除复制构造函数和复制赋值运算符:
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
这将阻止对象的复制,即禁止使用拷贝构造函数和复制赋值运算符进行对象的复制。
删除移动构造函数和移动赋值运算符:
MyClass(MyClass&&) = delete;
MyClass& operator=(MyClass&&) = delete;
这将阻止对象的移动语义,即禁止使用移动构造函数和移动赋值运算符进行对象的移动操作。
删除析构函数:
~MyClass() = delete;
这将阻止对象的销毁,即禁止调用析构函数进行对象的内存释放。
通过使用delete
关键字,可以在编译期间捕捉到一些潜在的错误或不正确的使用。例如,如果尝试复制或移动被删除的函数,编译器将会产生错误。
示例:
class MySingleton {
private:
MySingleton() = delete; // 默认构造函数被删除
MySingleton(const MySingleton&) = delete; // 复制构造函数被删除
MySingleton& operator=(const MySingleton&) = delete; // 复制赋值运算符被删除
public:
static MySingleton& getInstance() {
static MySingleton instance;
return instance;
}
void doSomething() {
// 执行操作
}
};
int main() {
MySingleton& singleton = MySingleton::getInstance();
singleton.doSomething();
// 错误示例,尝试创建被删除的默认构造函数的对象
// MySingleton singleton2; // 编译错误
return 0;
}
在上面的示例中,通过删除默认构造函数、复制构造函数和复制赋值运算符,实现了一个单例模式的类。这样,阻止了通过复制或拷贝方式创建多个实例。任何尝试复制的操作都会在编译时被捕获并产生错误。
总结来说,C++11的delete
关键字为开发者提供了更大的灵活性,可以显式地删除某些函数,以避免在特定情况下的误用和错误。这是C++中一个强大的特性,有助于更好地控制类的行为和语义。
在C++11标准中,关键字final
和override
用于类的继承和虚函数的重写。它们提供了一种显式的方法来控制和标记继承关系和函数重写。
final
关键字用于修饰类、虚函数或成员函数,表示它们不能被继承或重写。具体来说:
final
关键字表示该类不能被其他类继承。final
关键字表示该虚函数不能被派生类重写。final
关键字表示该成员函数不能在派生类中被重写。以下是使用final
关键字的示例:
class Base final {
// ...
};
class Derived : public Base { // 错误,无法继承被标记为final的类
// ...
};
class Base {
public:
virtual void foo() const final;
};
class Derived : public Base {
public:
void foo() const override; // 错误,无法重写被标记为final的虚函数
};
class Base {
public:
virtual void foo() const;
};
class Derived : public Base {
public:
void foo() const final; // 错误,无法在派生类中重写被标记为final的成员函数
};
override
关键字用于显式地标记派生类中的成员函数,以表明它们是基类中虚函数的重写版本。使用override
关键字可以增强代码的可读性和可维护性,并帮助编译器检测错误。如果派生类中的函数声明使用了override
关键字,但却没有重写基类中的虚函数,则会导致编译错误。
以下是使用override
关键字的示例:
class Base {
public:
virtual void foo() const{}
};
class Derived : public Base {
public:
void foo() const override{} // 表示该函数是基类虚函数的重写版本
};
class Base {
public:
virtual void foo() const{}
};
class Derived : public Base {
public:
void foo() const{} // 错误,没有使用override关键字重写基类的虚函数,编译器不会报错
};
用override
关键字的示例:
class Base {
public:
virtual void foo() const{}
};
class Derived : public Base {
public:
void foo() const override{} // 表示该函数是基类虚函数的重写版本
};
class Base {
public:
virtual void foo() const{}
};
class Derived : public Base {
public:
void foo() const{} // 错误,没有使用override关键字重写基类的虚函数,编译器不会报错
};
注意,在C++11之前,虚函数的重写是隐式的,不需要使用override
关键字。但是,使用override
关键字可以提供更明确的语义和更好的编译时错误检测。