C++类对象的创建方式

1、默认构造函数创建类对象

在C++中,当我们定义一个类时,如果没有明确地为类编写构造函数,编译器会为我们提供一个默认的构造函数。这就是我们所说的默认构造函数。默认构造函数没有任何参数,但是它会执行一些基本的任务,比如为类中的成员变量分配内存。

2、拷贝构造函数创建类对象

拷贝构造函数在C++中是一个特别重要的概念,它用于创建一个新的对象,作为已存在对象的副本。拷贝构造函数通常接收一个同类对象的引用作为参数。拷贝构造函数在很多情况下会被自动调用,比如函数参数的传递、函数返回值的返回等。因此,理解和正确使用拷贝构造函数在C++编程中至关重要。然而,随着C++新特性的引入,如移动构造函数,有时我们可以更有效率地创建类的对象,这将在后续小节中讲述。

3、赋值操作创建类对象

除了使用构造函数创建类的对象外,我们还可以通过赋值操作来创建新的类对象。这实际上涉及到了拷贝赋值操作符的使用。拷贝赋值操作符用于将一个对象的状态复制到另一个已经存在的对象。

eg.

class A
{
  public:
       int a;
       double b;
       A(): a(0), b(0.0) {}
       A(const A& pa): a(pa.a), b(pa.b) {}
       A& operator=(const A& pa)
       {
             if(this != &pa)
             {
                 a = pa.a;
                 b = pa.b;
             }
             return *this;
       }
};

使用赋值操作创建对象的方法如下:

A pa1;

A pa2 = pa1;

在这里,pa2是通过赋值操作创建的,它是pa1的一个副本。注意,这里的赋值操作符实际上调用的是拷贝构造函数,而不是拷贝赋值操作符。这是因为pa2在此语句中被定义,并且直接初始化。
而拷贝赋值操作符是用于已经存在的对象的赋值操作。例如:

A pa3;

pa3 = pa1; //这里使用的是拷贝赋值操作符,因为pa3已经存在

4、使用移动构造函数创建类对象

(1)右值引用

在C++11中,引入了右值引用的概念,标记为"&&"。右值引用主要用于标记临时对象,即那些即将被销毁、不能再赋值的对象。

临时对象主要包括函数返回的临时变量、表达式结果等。通过使用右值引用,我们可以避免在创建和删除临时变量时的资源浪费。

(2)移动构造函数

移动构造函数接收一个右值引用作为参数,它将拥有被引用对象的资源。原对象失去资源所有权,成为一个空对象。

相较于深拷贝,移动构造函数可以更高效地创建新的对象,因为它避免了资源的复制。

现在,让我们通过一个例子来了解如何使用移动构造函数创建类对象。

eg.

class B 
{
public:
    B(size_t size): b(size), ch(new char[size]) {}
    ~B() {delete[] ch};
    B(const B& other): b(other.len), ch(new char[other.len])
    {
       copy(other.ch, other.ch+other.len, ch);
    }
    B(B&& other): len(other.len), ch(other.ch)
    {
        other.len = 0;
        other.ch = NULL;
    }
    
private:
    size_t len;
    char* ch;
};

在上述代码中,我们定义了一个移动构造函数,它接受一个右值引用参数。在这个移动构造函数中,我们直接将"other"对象的资源(即"ch")移动到新创建的对象中,然后将"other"对象的资源指针置为nullptr,防止其析构函数释放资源。
下面是如何使用移动构造函数来创建B类对象的示例:

B buf1(100); //使用默认构造函数创建对象
B buf2(move(buf1)); //使用移动构造函数创建对象

这里,move(buf1)将"buf1"转为右值,触发了移动构造函数,将"buf1"的资源移动到"buf2",而非复制。此时,“buf1"变为空对象,其资源已经转移给了"buf2”。

5、使用委托构造函数创建类对象

委托构造函数的用法是在初始化列表中调用另一个构造函数。

eg.

class MyClass {
public:
    MyClass() : MyClass(0, 0) { 
        // 不再需要初始化代码 
    }

    MyClass(int a, int b) : a_(a), b_(b) { 
        // 初始化代码只在这里 
    }
private:
    int a_, b_;
};

值得注意的是,在使用委托构造函数时,被委托的构造函数必须在同一个类中,并且委托关系不能形成环。也就是说,我们不能让构造函数A委托给构造函数B,然后又让构造函数B委托给构造函数A。如果这样做,编译器会报错。

6、使用统一初始化语法创建类对象

统一初始化或列表初始化。它可以用于所有数据类型的初始化,包括基础类型、数组、结构体、容器等,也包括我们正在讨论的类对象。

eg.

class Person {
public:
    Person(std::string name, int age) : name_(name), age_(age) {}
private:
    std::string name_;
    int age_;
};

使用统一初始化语法,我们可以这样创建一个Person对象:

Person person{"张三", 25};

7、使用shared_ptr创建类对象
8、使用unique_ptr创建类对象
9、使用weak_ptr创建类对象

posted @ 2023-08-16 13:28  韓さん  阅读(2129)  评论(0编辑  收藏  举报