复制构造函数调用私有成员

本文介绍了一个简单的C++字符串类实现,重点讲解了构造函数如何进行深拷贝以确保对象之间的数据独立。通过实例演示了拷贝构造函数的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

class String{                                                                                                                       

private:
    char *m_data;
    int length;

public:
    String(const char *str)
    {   
        if (!str)
        {   
            length = 0;
            m_data = new char[1];
            *m_data = '\0';
        }   
        else
        {   
            length = strlen(str);
            m_data = new char[length + 1]; 
            strcpy(m_data, str);
        }   
    }   
        
    String(const String &str)
    {   
        length = strlen(str.m_data);
        m_data = new char[length + 1]; 
        strcpy(m_data, str.m_data);
        printf("String::String( const String &str)\n");
    }   
};
int main()
{
    String a("test");   
    String b(a);
    return 0;
} 
访问限制标号是针对类而不是针对一个类的不同对象,只要同属一个类就可以不用区分同一个类的不同对象。因为 是类的成员函数,所以有权限访问私有数据成员。如果是在main函数中直接,那肯定就会报错了,不能访问,因为这是在类外不能访问私有数据成员。一个类的成员函数可以访问这个类的私有数据成员,我们要理解这个对类的访问限制,而不是针对对象。
<think>我们正在讨论C++中的复制构造函数(Copy Constructor)。复制构造函数是一种特殊的构造函数,用于创建一个新对象作为现有对象的副本。它在以下情况下被调用: 1. 通过另一个同型的对象显式或隐式初始化一个对象。 2. 对象作为函数参数按值传递时。 3. 函数返回一个对象时(按值返回)。 复制构造函数的声明形式为:`ClassName(const ClassName &other);` 如果没有显式定义复制构造函数,编译器会自动生成一个默认的复制构造函数,该函数执行浅拷贝(逐个成员复制)。如果中有指针成员或需要管理资源(如动态内存),则必须自定义复制构造函数以实现深拷贝,避免浅拷贝导致的问题(如双重释放)。 下面我们通过一个示例来详细说明: ```cpp #include <iostream> #include <cstring> // for strcpy class String { private: char* data; int length; public: // 普通构造函数 String(const char* str) { length = strlen(str); data = new char[length + 1]; strcpy(data, str); } // 复制构造函数(深拷贝) String(const String& other) { length = other.length; data = new char[length + 1]; strcpy(data, other.data); } // 析构函数 ~String() { delete[] data; } // 打印字符串 void print() const { std::cout << data << std::endl; } }; int main() { String str1("Hello"); String str2 = str1; // 调用复制构造函数 str1.print(); // 输出: Hello str2.print(); // 输出: Hello return 0; } ``` 在上面的代码中: - 我们定义了一个`String`,它有一个指针成员`data`指向动态分配的字符数组。 - 我们定义了复制构造函数,它通过分配新的内存并复制内容来实现深拷贝。这样,每个对象都有自己的数据副本。 - 如果没有自定义复制构造函数,那么默认的复制构造函数会直接复制指针(即两个对象指向同一块内存),当两个对象析构时,同一块内存会被释放两次,导致未定义行为(通常是程序崩溃)。 注意:复制构造函数的参数必须是同型对象的常量引用(`const String&`),原因如下: 1. 使用引用避免无限递归(如果按值传递,会调用复制构造函数,从而再次按值传递,导致无限递归)。 2. 使用`const`因为复制构造函数不应该修改源对象。 ### 何时需要自定义复制构造函数? 当满足以下任一条件时,通常需要自定义复制构造函数: - 有指针成员并指向动态分配的内存。 - 有成员表示不可共享的资源(如文件句柄、网络连接等)。 ### 禁止复制 如果不想让对象被复制,可以将复制构造函数声明为私有并不提供实现(C++11以后可以用`=delete`)。 ```cpp class NonCopyable { private: NonCopyable(const NonCopyable&); // 只声明不实现 public: NonCopyable() {} }; // 或者使用C++11的delete class NonCopyable { public: NonCopyable() {} NonCopyable(const NonCopyable&) = delete; }; ``` §§相关问题§§: 1. 什么是浅拷贝和深拷贝?它们有什么区别? 2. 在C++中,什么时候会调用复制构造函数? 3. 如何防止一个复制? 4. C++11中的移动构造函数是什么?它与复制构造函数有何不同?</think># C++ 复制构造函数详解 ## 复制构造函数概念 复制构造函数是一种特殊的构造函数,用于创建一个新对象作为现有对象的副本。它在以下情况下被调用: 1. 使用一个对象初始化另一个对象时 2. 对象作为函数参数按值传递时 3. 函数返回对象时(按值返回) ## 基本语法 ```cpp class MyClass { public: // 复制构造函数声明 MyClass(const MyClass& other); }; ``` ## 完整示例代码 ```cpp #include <iostream> #include <cstring> // 用于字符串操作 class Person { private: char* name; int age; public: // 普通构造函数 Person(const char* n, int a) : age(a) { name = new char[strlen(n) + 1]; strcpy(name, n); std::cout << "普通构造函数: " << name << std::endl; } // 复制构造函数(深拷贝实现) Person(const Person& other) : age(other.age) { name = new char[strlen(other.name) + 1]; strcpy(name, other.name); std::cout << "复制构造函数: " << name << std::endl; } // 析构函数 ~Person() { delete[] name; std::cout << "析构函数: " << name << std::endl; } // 显示信息 void display() const { std::cout << "姓名: " << name << ", 年龄: " << age << std::endl; } }; // 函数演示按值传递对象 void displayPerson(Person p) { std::cout << "函数内: "; p.display(); } int main() { // 情况1: 使用一个对象初始化另一个对象 Person person1("张三", 25); Person person2 = person1; // 调用复制构造函数 std::cout << "\n对象信息:\n"; person1.display(); person2.display(); // 情况2: 按值传递对象 std::cout << "\n按值传递演示:\n"; displayPerson(person1); // 情况3: 函数返回对象 std::cout << "\n函数返回对象演示:\n"; Person createPerson() { Person temp("李四", 30); return temp; // 可能调用复制构造函数(取决于编译器优化) } Person person3 = createPerson(); person3.display(); return 0; } ``` ## 关键概念解释 ### 1. 浅拷贝 vs 深拷贝 - **浅拷贝**:默认复制构造函数执行浅拷贝(简单复制指针值) - **深拷贝**:需要自定义复制构造函数复制指针指向的内容(如上例中的`name`) ### 2. 必须自定义复制构造函数的情况 当包含以下成员时,必须自定义复制构造函数: - 原始指针(管理动态内存) - 文件句柄 - 网络连接 - 其他不可共享的资源 ### 3. 复制构造函数参数 - **必须使用常量引用**:`const MyClass& other` - 使用值传递会导致无限递归(创建临时对象时又调用复制构造函数) ### 4. 编译器行为 - 如果未定义复制构造函数,编译器会自动生成一个(执行浅拷贝) - 当有指针成员时,默认生成的复制构造函数通常不安全 ## 输出结果说明 运行上述代码将显示: ``` 普通构造函数: 张三 复制构造函数: 张三 对象信息: 姓名: 张三, 年龄: 25 姓名: 张三, 年龄: 25 按值传递演示: 复制构造函数: 张三 函数内: 姓名: 张三, 年龄: 25 析构函数: 张三 函数返回对象演示: 普通构造函数: 李四 复制构造函数: 李四 // 可能被编译器优化掉 析构函数: 李四 姓名: 李四, 年龄: 30 析构函数: 李四 析构函数: 张三 析构函数: 张三 ``` ## 最佳实践 1. **三法则**:如果需要自定义析构函数,通常也需要自定义复制构造函数复制赋值操作符 2. **使用`const`引用**:确保复制构造函数不修改源对象 3. **资源管理**:对动态分配的资源进行深拷贝 4. **考虑禁用复制**:对于不可复制的对象,使用`= delete`禁用复制构造函数 ## 禁用复制构造函数 对于不可复制(如管理独占资源的): ```cpp class NonCopyable { public: NonCopyable() = default; // 禁用复制构造函数 NonCopyable(const NonCopyable&) = delete; // 禁用复制赋值操作符 NonCopyable& operator=(const NonCopyable&) = delete; }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值