一)构造函数的概念
构造函数是一种特殊的成员函数,用于在创建对象时初始化对象的数据成员。它的主要目的是确保对象在使用前被正确地初始化,使得对象处于一个合理的初始状态。构造函数的名称与类名相同,没有返回类型(包括void)。例如,对于一个名为MyClass的类,其构造函数可以写成MyClass()或者MyClass(int parameter)等形式。
二)默认构造函数的定义和特点
定义:
默认构造函数是一种可以在没有显式提供初始化值的情况下调用的构造函数。它有两种形式:一种是编译器自动生成的默认构造函数,另一种是用户自己定义的默认构造函数(无参数构造函数或者所有参数都有默认值的构造函数)。
编译器自动生成的默认构造函数:
当类中没有定义任何构造函数时,编译器会自动为该类生成一个默认构造函数。这个默认构造函数会对类的基本数据类型成员进行默认初始化。对于基本数据类型的成员变量,在不同的存储类别下初始化行为有所不同。例如,在全局或静态存储区域的对象,基本数据类型的成员变量会被初始化为 0;而对于自动存储区域(如函数内部定义的局部对象)的基本数据类型成员变量,它们的值是未定义的(缺点)。
例如,对于以下类:
class MyClass
{
public:int num;double value;
};
如果创建一个全局对象MyClass globalObj;,那么globalObj.num会被初始化为 0,globalObj.value也会被初始化为 0。但如果在函数内部创建一个局部对象MyClass localObj;,localObj.num和localObj.value的值是未定义的。
用户自定义的默认构造函数:
用户可以自己定义默认构造函数来满足特定的初始化需求。用户自定义的默认构造函数有两种情况:一是无参数的构造函数,二是所有参数都有默认值的构造函数。
例如,无参数的默认构造函数:
class MyClassWithCustomConstructor
{
private:int num;
public:MyClassWithCustomConstructor() {num = 0;}
};
在这个例子中,当创建MyClassWithCustomConstructor对象时,如MyClassWithCustomConstructor obj;,对象的num成员会被初始化为 0。
所有参数都有默认值的构造函数示例:
class AnotherClass
{
private:int num;double value;
public:AnotherClass(int n = 0, double v = 0.0){num = n;value = v;}
};
对于AnotherClass,可以通过AnotherClass obj;(此时num为 0,value为 0.0)或者AnotherClass obj(5);(此时num为 5,value为 0.0)等方式来创建对象,并且对象会根据提供的参数或者默认值进行初始化。
初始化列表方式的默认构造函数(推荐用于成员变量初始化)
语法和示例:
初始化列表是在构造函数的参数列表后面紧跟一个冒号(:),然后是成员变量初始化列表,每个成员变量用逗号隔开。
例如,对于Person类,使用初始化列表的默认构造函数可以这样写:
class Person
{
private:std::string name;int age;
public:Person() : name("Unknown"), age(0) {}
};
这种方式直接在构造函数开头就对成员变量进行初始化,对于一些特殊类型的成员变量(如const成员变量、引用成员变量),必须使用初始化列表进行初始化。而且,使用初始化列表通常效率更高,因为它是在对象创建时直接初始化成员变量,而不是先默认初始化成员变量,再在构造函数体中进行赋值操作。
三)创建类对象的几种方式
1)使用默认构造函数创建对象
无参数的默认构造函数方式
定义和特点:当类定义了一个无参数的默认构造函数时,可以直接使用类名加空括号来创建对象。这种方式简单直接,适用于对象的初始化逻辑相对固定,不需要外部传入参数来改变初始状态的情况。
class Circle
{
private:double radius;
public:Circle(){radius = 1.0;}double getRadius() {return radius;}
};
int main()
{Circle c;std::cout << "Radius of the circle: " << c.getRadius() << std::endl;return 0;
}
在这个例子中,Circle类有一个无参数的默认构造函数,在构造函数中radius被初始化为 1.0。在main函数中,通过Circle c;创建了一个Circle对象,然后可以使用对象的成员函数getRadius来获取半径的值。
所有参数都有默认值的构造函数方式(也可视为默认构造函数)
定义和特点:这种构造函数带有参数,但每个参数都有默认值。它既可以像无参数的默认构造函数那样,在不传入任何参数时使用默认值进行初始化,也可以根据需要传入部分或全部参数来覆盖默认值进行初始化。这种方式提供了更大的灵活性,使得对象的初始化可以根据不同的场景进行调整。
class Rectangle
{
private:int width;int height;
public:Rectangle(int w = 2, int h = 3) {width = w;height = h;}int getArea() {return width * height;}
};
int main()
{// 使用默认值创建对象Rectangle r1;// 传入部分参数创建对象Rectangle r2(4);// 传入全部参数创建对象Rectangle r3(5, 6);std::cout << "Area of r1: " << r1.getArea() << std::endl;std::cout << "Area of r2: " << r2.getArea() << std::endl;std::cout << "Area of r3: " << r3.getArea() << std::endl;return 0;
}
这里Rectangle类的构造函数有两个参数,每个参数都有默认值。在main函数中,通过不同的方式创建了三个Rectangle对象,展示了这种构造函数的灵活性。
2)使用带参数的构造函数创建对象(是所有参数都有默认值的构造函数方式的简化版本)
定义和特点:当类的构造函数带有参数时,需要在创建对象时传入相应的参数,这些参数用于初始化对象的成员变量。这种方式可以根据具体的参数值来创建具有不同初始状态的对象,使得对象的创建更加灵活,能够满足各种不同的需求。
class Point
{
private:int x;int y;
public:Point(int a, int b) {x = a;y = b;}void print() {std::cout << "Point(" << x << ", " << y << ")" << std::endl;}
};
int main()
{Point p(3, 4);p.print();return 0;
}
在这个Point类中,构造函数接受两个参数,用于初始化x和y成员变量。在main函数中,通过Point p(3, 4);创建了一个Point对象,其中x被初始化为 3,y被初始化为 4,然后可以使用print函数来输出点的坐标。
3)使用拷贝构造函数创建对象(浅拷贝和深拷贝)
浅拷贝定义和示例:
定义:拷贝构造函数是一种特殊的构造函数,用于使用一个已存在的同类型对象来初始化新创建的对象。浅拷贝是指在拷贝对象时,对于对象中的指针或引用类型的成员,只是简单地复制指针或引用的值,而不是复制它们所指向或引用的内容。
class ShallowCopyExample
{
private:int* data;
public:ShallowCopyExample(int d) {data = new int(d);}ShallowCopyExample(const ShallowCopyExample& other) {data = other.data;}~ShallowCopyExample() {delete data;}
};
int main()
{ShallowCopyExample obj1(5);ShallowCopyExample obj2 = obj1;return 0;
}
在这个例子中,ShallowCopyExample类有一个指针成员data。拷贝构造函数进行了浅拷贝,只是复制了指针的值。当对象被销毁时,会出现问题,因为obj1和obj2的data指针指向同一块内存,两次调用delete会导致错误。
深拷贝定义和示例:
定义:深拷贝是指在拷贝对象时,对于对象中的指针或引用类型的成员,会复制它们所指向或引用的内容,而不是仅仅复制指针或引用的值。这样可以避免多个对象共享同一块内存导致的问题。
class DeepCopyExample
{
private:int* data;
public:DeepCopyExample(int d) {data = new int(d);}DeepCopyExample(const DeepCopyExample& other) {data = new int(*other.data);}~DeepCopyExample() {delete data;}
};
int main()
{DeepCopyExample obj1(5);DeepCopyExample obj2 = obj1;return 0;
}
这里的拷贝构造函数进行了深拷贝,obj2的data指针指向了一块新的内存,这块内存中的内容是从obj1的data所指向的内存中复制过来的。这样在对象销毁时,就不会出现前面浅拷贝的问题。
4)使用动态内存分配创建对象(通过new操作符)
定义和特点:使用new操作符可以在堆上动态地分配内存来创建对象。这种方式创建的对象在内存中的生命周期可以由程序员控制,不会像在栈上创建的对象那样,在离开作用域时自动销毁。这对于需要在一个函数中创建对象,但又希望对象在函数结束后仍然存在的情况非常有用。
class DynamicObject
{
private:int value;
public:DynamicObject(int v) {value = v;}int getValue() {return value;}
};
int main()
{DynamicObject* ptr = new DynamicObject(7);std::cout << "Value of the object: " << ptr->getValue() << std::endl;delete ptr;return 0;
}
在这个例子中,通过new操作符创建了一个DynamicObject类的对象,并将对象的指针存储在ptr中。可以通过指针来访问对象的成员函数。最后,使用delete操作符来释放对象占用的内存。
如果你看到这里有点迷惑,这就对了,学习应该是越学越迷惑,如果一学就明白,说明你没有学到知识。后面您还会遇到匿名对象,总之,好像一下子记住好多东西很难,那么就不用强迫自己去记忆,多看、多理解,然后不知不觉就懂了。