重学C++系列之构造函数,附案例

一、什么是构造函数

        当一个对象在创建的时候,为了可以同时对该对象的成员变量进行初始化,保证其成员变量值的安全性,C++编译器提供了一个函数,构造函数来解决这个问题。

二、构造函数的特点

1、构造函数也是函数,但函数名必须跟类名相同

2、构造函数无返回值

3、构造函数可以重载

4、在创建类对象时,构造函数会自动隐式调用

5、构造函数初始化列表的执行时间比在构造函数体中进行赋值操作的要早,同时效率更高。

6、系统默认提供了两个默认构造函数,一个是普通的构造函数,另外一个是拷贝构造函数,当用户手动声明时,系统不会提供对应的默认构造函数。

三、普通构造函数

1、使用C++默认提供的构造函数

#include <iostream>

using namespace std;

class Test
{
    int a;
    public:
    void setA(int a)
    {
        this->a = a;
    }
    int getA()
    {
        return a;
    }
};


int main()
{
    Test A; // 使用默认的普通构造函数创建对象
    A.setA(10);
    cout << "a = " << A.getA() << endl;

    return 0;
}

 2、使用自己声明的构造函数

        定义构造函数时,注意必须把所有非静态成员变量进行初始化,否则无法编译通过。

#include <iostream>

using namespace std;

class Test
{
    int a;
    public:
    Test()  // 自己定义了一个构造函数
    {
        cout << "my Test" << endl;
    }
    void setA(int a)
    {
        this->a = a;
    }
    int getA()
    {
        return a;
    }
};


int main()
{
    Test A; // 使用自己的普通构造函数创建对象
    A.setA(10);
    cout << "a = " << A.getA() << endl;

    return 0;
}

3、构造函数重载

      1、带默认参数声明构造函数时,应该避免声明无参的构造函数

#include <iostream>

using namespace std;

class Test
{
    int a;
    public:
    Test()
    {
        cout << "Test()" << endl;
    }
    Test(int a = 0)     // 这种带默认参数的重载是会模糊的
    {
        cout << "Test(int a = 0)" << endl;
    }
    
    void setA(int a)
    {
        this->a = a;
    }
    int getA()
    {
        return a;
    }
};


int main()
{
    Test A;         // 这种声明方式无法确定是调用Test()还是Test(int a = 0)
    A.setA(10);
    
    cout << "A.a = " << A.getA() << endl;
    // cout << "B.a = " << B.getA() << endl;

    return 0;
}

        2、建议的构造函数声明方式

#include <iostream>

using namespace std;

class Test
{
    int a;
    public:
  
    Test(int a = 0)     // 有几个成员变量就参数列表就应该有几个
    {
        cout << "Test(int a = 0)" << endl;
        this->a = a;
    }
    
    void setA(int a)
    {
        this->a = a;
    }
    int getA()
    {
        return a;
    }
};


int main()
{
    Test A;         
    Test B(10);
    
    cout << "A.a = " << A.getA() << endl;
    cout << "B.a = " << B.getA() << endl;

    return 0;
}

四、拷贝构造函数

1、函数原型

        类名 (const 类名 & other)

2、为什么函数原型不能是 类名 (const 类名 other)

        如果采用该函数原型 类名 (const 类名 other),在传参时,要将实参传递给形参,相当于创建一个新对象,此时要调用拷贝构造函数,而在调用拷贝构造函数时,又要将本次的形参传递给下一个调用构造函数的形参,无限循环。

        函数原型为 类名(const 类名 &other)情况:

#include <iostream>

using namespace std;

class Test
{
    int a;
    public:
    Test()
    {
        cout << "Test()" << endl;
    }
    Test(const Test & other)    // 拷贝构造函数
    {
        cout << "Test(const Test & other)" << endl;
        a = other.a;
    }
    void setA(int a)
    {
        this->a = a;
    }
    int getA()
    {
        return a;
    }
};


int main()
{
    Test A;
    A.setA(10);
    Test B = A;     // 使用类对象来初始化同类的另一个对象
    cout << "A.a = " << A.getA() << endl;
    cout << "B.a = " << B.getA() << endl;

    return 0;
}

        函数原型为 类名(const 类名 other)情况:

#include <iostream>

using namespace std;

class Test
{
    int a;
    public:
    Test()
    {
        cout << "Test()" << endl;
    }
    Test(const Test other)    // 无法编译,错误的函数原型
    {
        cout << "Test(const Test & other)" << endl;
        a = other.a;
    }
    void setA(int a)
    {
        this->a = a;
    }
    int getA()
    {
        return a;
    }
};


int main()
{
    Test A;
    A.setA(10);
    Test B = A;     // 使用类对象来初始化同类的另一个对象
    cout << "A.a = " << A.getA() << endl;
    cout << "B.a = " << B.getA() << endl;

    return 0;
}

3、使用场景

1)、当用一个类对象去初始化同类的另一个对象。

2)、如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的拷贝构造函数将被调用。

3)、如果函数的返回值是类A的对象时,则函数返回时,A的拷贝构造函数将被调用。(待验证,跟编译器版本有关,现在的大多都是调用构造函数),但是返回是对象的引用时,一定调用拷贝构造函数。

        当用一个类对象去初始化同类的另一个对象。案例:

#include <iostream>
 
using namespace std;
 
class Test
{
    int a;
public:
    Test(int a = 0)
    {
        cout << "Test(int a = 0)" << endl;
        this->a = a;
    }
    Test(const Test &other)
    {
        cout << "Test(const Test& other)" << endl;
        this->a = other.a;
    }
    void setA(int a)
    {
        this->a = a;
    }
    int getA()
    {
        return a;
    }
};
 
 
int main()
{
    Test A;
    A.setA(10);
    Test B = A;     // 使用类对象来初始化同类的另一个对象
    cout << "A.a = " << A.getA() << endl;
    cout << "B.a = " << B.getA() << endl;
 
    return 0;
}

        如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的拷贝构造函数将被调用。案例

#include <iostream>

using namespace std;

class Test
{
    int a;
   
    public:
  
    Test(int a = 0)     // 普通构造函数
    {
        cout << "Test(int a = 0)" << endl;
        this->a = a;
    }
    Test(const Test &other) // 拷贝构造函数
    {
        cout << "Test(const Test &other)" << endl;
        this->a = other.a;
    }
    
    void show()
    {
        cout << "a = " << a << endl;
    }
};

void func(Test B)
{
    cout << "void func(Test B)" << endl;
    B.show();
}


int main()
{
    Test A(10);         

    A.show();
    func(A);
 
    return 0;
}

        如果函数的返回值是类A的对象时,则函数返回时,A的拷贝构造函数将被调用。(待验证,跟编译器版本有关,现在的大多都是调用构造函数)案例:

#include <iostream>
#include <cstring>

using namespace std;

class Test
{
    int a;
   
public:
    Test(int a = 0)     // 普通构造函数
    {
        cout << "Test(int a = 0)" << endl;
        this->a = a;
    }
    Test(const Test &other) // 拷贝构造函数
    {
        cout << "Test(const Test &other)" << endl;
        this->a = other.a;
    }
   
    void show()
    {
        cout << "a = " << a << endl;
    }
};

// 返回对象
Test func1()
{
    cout << "Test func1()" << endl;
    Test B(10);
    return B;
}

// 返回对象的引用
Test& func2()
{
    cout << "Test& func2()" << endl;
    Test *p = new Test(100);
    return *p;
}

int main()
{

    Test B = func1();
    B.show();

    Test C = func2();
    C.show();

    return 0;
}

五、一些特殊情况下定义构造函数的方式

1、成员变量是const修饰时

        const 修饰变量时,保证了该变量在声明时就应该立即初始化。在构造函数中有特殊的声明方式:类名 (参数类型 参数1, 参数类型 参数2,...):成员变量1(参数1),成员变量2(参数2)...

#include <iostream>

using namespace std;

class Test
{
    int a;
    const int b;
    const int c;
    public:
  
    Test(int a = 0, const int _b = 0, const int _c = 0):b(_b), c(_c)     // 有几个成员变量就参数列表就应该有几个
    {
        this->a = a;
        // this->b = _b;   // 这行不用写,只用在参数列表冒号后面写就行
        // this->c = _c
    }
    
    void show()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
        cout << "c = " << c << endl;
    }
};


int main()
{
    Test A(10, 100, 1000);         

    A.show();
 
    return 0;
}

2、成员变量是指针类型时

        不能直接把指针赋值给指针,指针类型的成员变量需要重新申请一个空间,这样在释放对象时,不会对同一个空间重复释放多次。

#include <iostream>
#include <cstring>

using namespace std;

class Test
{
    int a;
    char *b;
   
    public:
  
    Test(int a = 0)     // 普通构造函数
    {
        cout << "Test(int a = 0)" << endl;
        this->a = a;
        this->b = nullptr;  // 创建对象时,指针指向空
    }
    Test(const Test &other) // 拷贝构造函数
    {
        cout << "Test(const Test &other)" << endl;
        this->a = other.a;
        this->b = new char(strlen(other.b)+1);  // 重新申请一份堆空间
        strcpy(this->b, other.b);
        this->b[strlen(other.b)] = '\0';
        
    }
    void setB(char *str)
    {
        this->b = new char(strlen(str)+1);  // 重新申请一份堆空间
        strcpy(this->b, str);
        this->b[strlen(str)] = '\0';
    }
    
    void show()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl;
    }
};



int main()
{
    Test A(10);    
    A.setB((char*)"Hello World");     
    A.show();

    Test B = A;
    B.show();
 
    return 0;
}

 3、成员变量的类型是类类型时

#include <iostream>
#include <cstring>

using namespace std;

class Test
{
    int a;
   
public:
    Test(int a = 0)     // 普通构造函数
    {
        cout << "Test(int a = 0)" << endl;
        this->a = a;
    }
    Test(const Test &other) // 拷贝构造函数
    {
        cout << "Test(const Test &other)" << endl;
        this->a = other.a;
    }
   
    void show()
    {
        cout << "a = " << a << endl;
    }
};

class Data
{
    Test test;
    int c;
public:
    Data(Test _test = Test(40), int _c = 0) // 建议在声明时显示调用构造函数
    {
        cout << "Data(Test _test = Test(), int _c = 0)" << endl;
        test = _test;
        c = _c;
    }

    void show()
    {
        cout << "Data::show()" << endl;
        test.show();
        cout << "c = " << c << endl;
    }

};

int main()
{

    Data B;
    B.show(); 

    return 0;
}

六、总结

        以上就是构造函数的介绍,有些结论可能不是一样的,跟编译器版本有关,但是如果是面试的话,建议直接讲结论。 关于更多有关构造函数的,如派生类,继承那些构造函数放在后面讲。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Kernel Ridge Regression C++类实现及使用案例: ``` #include <Eigen/Core> #include <Eigen/Dense> class KernelRidgeRegression { public: KernelRidgeRegression(double lambda, const std::string& kernel_type) : lambda_(lambda), kernel_type_(kernel_type) {} void fit(const Eigen::MatrixXd& X, const Eigen::VectorXd& y) { X_ = X; y_ = y; int n_samples = X_.rows(); Eigen::MatrixXd K(n_samples, n_samples); if (kernel_type_ == "linear") { K = X_ * X_.transpose(); } else if (kernel_type_ == "rbf") { K.resize(n_samples, n_samples); for (int i = 0; i < n_samples; ++i) for (int j = i; j < n_samples; ++j) K(i, j) = K(j, i) = exp(-1.0 * (X_.row(i) - X_.row(j)).squaredNorm() / (2 * sigma_ * sigma_)); } Eigen::MatrixXd I(n_samples, n_samples); I.setIdentity(); alpha_ = (K + lambda_ * I).ldlt().solve(y_); } double predict(const Eigen::VectorXd& x) { int n_samples = X_.rows(); Eigen::VectorXd k(n_samples); if (kernel_type_ == "linear") { k = X_ * x; } else if (kernel_type_ == "rbf") { k.resize(n_samples); for (int i = 0; i < n_samples; ++i) k(i) = exp(-1.0 * (X_.row(i) - x).squaredNorm() / (2 * sigma_ * sigma_)); } return k.dot(alpha_); } private: double lambda_; std::string kernel_type_; double sigma_ = 1.0; Eigen::MatrixXd X_; Eigen::VectorXd y_; Eigen::VectorXd alpha_; }; int main() { Eigen::MatrixXd X(10, 2); Eigen::VectorXd y(10); for (int i = 0; i < 10; ++i) { X(i, 0) = i; X(i, 1) = i * i; y(i) = i * i * i; } KernelRidgeRegression krr(0.1, "rbf"); krr.fit(X, y); Eigen::VectorXd x_test(2); x_test << 1, 1; std::cout << "Prediction: " << krr.predict(x_test) << std::endl; return 0; } ``` 此处实现了一个带有线性和RBF核的Kernel Ridge Regression类。构造函数需要输入正则化参数lambda和核函数类型,可以选择"linear"或"rbf",默认为"rbf"。fit函数需要输入特征矩阵X和目标向量y,用于计算alpha系数。predict函数需要输入一个样本向量x,用于预测其目标值。 在这个示例中,我们使用二维特征和目标值来演示,首先创建一个10x2的特征矩阵和一个10维的目标向量。然后创建一个KernelRidgeRegression对象,并使用fit函数拟合训练数据。最后,使用predict函数预测一个新样本的目标值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值