c++(强生成关键字+可变参数模板+emplace)[26]

强制生成 不生成

在C++中,可以通过一些方式来控制编译器是否生成某些特殊成员函数(如默认构造函数、拷贝构造函数、拷贝赋值运算符、析构函数等)。

  1. 默认生成:如果你没有显式地定义这些特殊成员函数,编译器会自动生成它们。这被称为默认生成。默认生成的成员函数会根据类的特性进行生成,例如默认构造函数会生成一个无参构造函数,拷贝构造函数会生成一个按值拷贝的构造函数。

  2. 强制生成:如果你显式地声明了某个特殊成员函数,但是不提供其定义,编译器将不会生成该函数的默认实现。这被称为强制生成。通过强制生成,你可以禁止某些特殊成员函数的自动生成,从而实现特定的语义或行为。

  3. 不生成:如果你显式地删除了某个特殊成员函数,编译器将不会生成该函数的默认实现。这被称为不生成。通过不生成,你可以禁止某些特殊成员函数的使用,从而防止不希望的行为或错误。

下面是一些示例代码,展示了如何强制生成或不生成特殊成员函数:

class MyClass {
public:
    // 强制生成默认构造函数
    MyClass() = default;

    // 强制生成拷贝构造函数
    MyClass(const MyClass& other) = default;

    // 不生成拷贝赋值运算符
    MyClass& operator=(const MyClass& other) = delete;

    // 强制生成析构函数
    ~MyClass() = default;
};

在上面的示例中,我们通过使用default关键字来强制生成默认构造函数、拷贝构造函数和析构函数。同时,我们通过使用delete关键字来不生成拷贝赋值运算符。

需要注意的是,强制生成或不生成特殊成员函数是一种高级用法,需要谨慎使用。你应该根据具体的需求和设计来决定是否需要强制生成或不生成某些特殊成员函数。

// 以下代码在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对象拷贝)
	Person(const Person& p) = delete;
	
		~Person()
		{
			cout << "~Person()" << endl;
		}
	
	private:
		bit::string _name; // 自定义类型
		int _age = 1;		   // 内置类型
	};
	
	int main()
	{
		Person s1("张三", 18);
		Person s2 = s1;
		Person s3 = std::move(s1);
		cout << endl << endl;
		Person s4;
		s4 = std::move(s2);
	
		return 0;
	}

final || override

在C++中,finaloverride是两个关键字,用于对类的成员函数进行修饰和控制。

  1. final关键字:final用于修饰类、虚函数和成员函数,表示它们不能被继承、重写或覆盖。当一个类被声明为final时,其他类就不能从该类派生。当一个虚函数被声明为final时,它不能在派生类中被重写或覆盖。当一个非虚函数被声明为final时,它不能在派生类中被重新定义。
class Base final {
    // ...
};

class Derived : public Base {  // 错误,Base类被声明为final,不能被继承
    // ...
};

class Base {
public:
    virtual void foo() final {
        // ...
    }
};

class Derived : public Base {
public:
    void foo() override {  // 错误,foo函数被声明为final,不能被重写
        // ...
    }
};
  1. override关键字:override用于修饰派生类中的虚函数,表示该函数是对基类中的虚函数的重写。使用override可以提高代码的可读性和安全性,确保派生类中的函数与基类中的虚函数具有相同的签名。
class Base {
public:
    virtual void foo() {
        // ...
    }
};

class Derived : public Base {
public:
    void foo() override {  // 明确标记为重写虚函数
        // ...
    }
};

如果在派生类中使用了override关键字修饰一个函数,但该函数并不是基类中的虚函数,编译器将会报错。

总之,finaloverride是C++11引入的关键字,用于对类的成员函数进行修饰和控制。final表示类或函数不能被继承、重写或覆盖,而override表示派生类中的函数是对基类中虚函数的重写。

要求delete关键字实现,一个类,只能在堆上创建对象

当构造函数被声明为私有时,它只能在类的内部被访问。这意味着在类的外部无法直接调用构造函数来创建对象。因此,将构造函数设为私有可以阻止类在栈上创建对象的方式。

在C++中,当你在栈上声明一个对象时,编译器会在编译时自动调用类的构造函数来创建对象。但是,如果构造函数是私有的,编译器将无法调用该构造函数,从而阻止了在栈上创建对象的方式。

以下是一个示例代码,展示了私有构造函数如何阻止类在栈上创建对象的方式:

class MyClass {
private:
    MyClass() {}  // 私有的构造函数

public:
    void doSomething() {
        // 对象的成员函数
    }
};

int main() {
    MyClass obj;  // 编译错误!无法调用私有构造函数

    return 0;
}

在上面的示例中,我们将构造函数声明为私有的,然后在main()函数中尝试在栈上创建一个MyClass对象。由于构造函数是私有的,编译器将无法调用该构造函数,从而导致编译错误。

需要注意的是,尽管无法在栈上直接创建对象,但仍然可以通过其他方式(如静态成员函数或友元函数)在堆上创建对象。

class HeapOnly
{
public:
	~HeapOnly() = delete;
};
int main()
{
	//HeapOnly hp1;
	//static HeapOnly hp2;          //自定义类型会调用析构

	HeapOnly* ptr = new HeapOnly;  //指针不会调用析构

	return 0;
}

下面例子此时会有资源泄露

class HeapOnly
{
public:
	HeapOnly()
	{
		_str = new char[10];
	}
	~HeapOnly() = delete;
private:
	char* _str;
};
int main()
{
	//HeapOnly hp1;
	//static HeapOnly hp2;          //自定义类型会调用析构

	HeapOnly* ptr = new HeapOnly;  //指针不会调用析构
	//delete ptr;                  //无析构 不能delete
	return 0;
}

自写destroy解决

// 要求delete关键字实现,一个类,只能在堆上创建对象
class HeapOnly
{
public:
	HeapOnly()
	{
		_str = new char[10];
	}
	~HeapOnly() = delete;
	void Destroy()
	{
		delete[] _str; 
		operator delete(this);
	}
private:
	char* _str;
};
int main()
{
	//HeapOnly hp1;
	//static HeapOnly hp2;          //自定义类型会调用析构

	HeapOnly* ptr = new HeapOnly;  //指针不会调用析构
	//delete ptr;                  //无析构 不能delete
	ptr->Destroy();

	return 0;
}

可变参数模板

c++(强生成关键字+可变参数模板+emplace)[26]_第1张图片

//可变参数的函数模板
template <class ...Args>
void ShowList(Args... args)
{
	cout<< sizeof...(args) <<endl;  //计算几个参数
}
int main()
{
	string str("hello");
	ShowList(1);
	ShowList();
	ShowList(1, 2);
	ShowList(1, 'A', str);

	return 0;
}

c++(强生成关键字+可变参数模板+emplace)[26]_第2张图片
递归看参数

//可变参数的函数模板
void ShowList()
{
	cout << endl;
}

template <class T, class ...Args>
void ShowList(const T& val, Args... args)
{
	cout << val << " ";
	ShowList(args...);
	//cout<< sizeof...(args) <
}
int main()
{
	string str("hello");
	ShowList(1);
	ShowList();
	ShowList(1, 2);
	ShowList(1, 'A', str);

	return 0;
}

结果

1

1 2
1 A hello

列表初始化获取

template<class T>
int PrintArg(const T& x)
{
	cout << x << " ";
	return 0; 
}
template <class ...Args>
void ShowList(Args... args)
{
	int a[] = { PrintArg(args)... };
	cout << endl;
}
int main()
{
	string str("hello");
	ShowList(1);
	ShowList();
	ShowList(1, 2);
	ShowList(1, 'A', str);

	return 0;
}

emplace

emplace_back()是C++标准库容器(如vector、list和deque)提供的一个成员函数,用于在容器的末尾直接构造对象,而不需要显式地调用构造函数。

emplace_back()的优势主要体现在以下几个方面:

  1. 减少对象的拷贝或移动:使用emplace_back()可以直接在容器的末尾构造对象,而不需要先创建一个临时对象,然后再将其拷贝或移动到容器中。这样可以减少不必要的对象拷贝或移动操作,提高代码效率。

  2. 简化代码:相比于显式地调用构造函数和push_back()函数,使用emplace_back()可以更简洁地创建对象并将其添加到容器中。你只需要传递构造函数所需的参数,而不需要创建临时对象或手动调用构造函数。

  3. 支持完美转发:emplace_back()函数支持完美转发,可以将参数直接传递给构造函数,无需手动进行类型转换。这意味着你可以使用不同类型的参数来构造对象,而不需要为每个类型编写重载的构造函数。

以下是一个使用emplace_back()的示例代码:

#include 

class MyClass {
public:
    MyClass(int x, int y) {
        // 构造函数的实现
    }
};

int main() {
    std::vector<MyClass> myVector;
    myVector.emplace_back(10, 20);  // 在容器的末尾构造对象

    return 0;
}

在上面的示例中,我们使用emplace_back()函数在myVector容器的末尾直接构造了一个MyClass对象,而不需要先创建一个临时对象。

总而言之,emplace_back()函数可以提高代码的效率和可读性,减少对象的拷贝或移动,并支持完美转发,使代码更加简洁和灵活。

emplace支持参数包
c++(强生成关键字+可变参数模板+emplace)[26]_第3张图片

定位new

内存池 显示的调用构造函数

你可能感兴趣的:(C++,c++,开发语言)