C++ Programming – Non-static member initialization


Trong C++ 11, chúng ta có thể thiết lập các giá trị mặc định cho các biến member một cách trực tiếp như sau (không sử dụng từ khóa static):

class Rectangle
{
private:
    double m_length = 1.0; // m_length has a default value of 1.0
    double m_width = 1.0; // m_width has a default value of 1.0
 
public:
    Rectangle()
    {
    // This constructor will use the default values above since they aren't overridden here
    }
 
    void print()
    {
        std::cout << "length: " << m_length << ", width: " << m_width << '\n';
    }
};
 
int main()
{
    Rectangle x; // x.m_length = 1.0, x.m_width = 1.0
    x.print();
 
    return 0;
}

Kết quả của chương trình trên như sau:
length: 1.0, width: 1.0

Trên đây là cách chúng ta dùng để thiết lập giá trị mặc định cho các biến member trong một class.

Chúng ta sẽ qua một khái niệm mới.
Nom-static member initialization (hay còn gọi là in-class member initializers) cung cấp các giá trị mặc định cho các biến member của class. Và khi constructor không cung cấp các giá trị mặc định (xem bài trước), thì constructor sẽ lấy giá trị mặc định mà bạn đã thiết lập lúc ban đầu (thông qua member initialization list).
Nhưng cần chú ý rằng constructor vẫn sẽ xác định loại của object được tạo là như thế nào. Xem ví dụ sau:

class Rectangle
{
private:
    double m_length = 1.0;
    double m_width = 1.0;
 
public:
 
    // note: No default constructor provided in this example
 
    Rectangle(double length, double width)
        : m_length(length), m_width(width)
    {
        // m_length and m_width are initialized by the constructor (the default values aren't used)
    }
 
    void print()
    {
        std::cout << "length: " << m_length << ", width: " << m_width << '\n';
    }
 
};
 
int main()
{
    Rectangle x; // will not compile, no default constructor exists, even though members have default initialization values
 
    return 0;
}

Mặc dù chúng ta đã cung cấp giá trị mặc định cho tất cả các member, và trong đoạn code trên không có default constructor, vì vậy chúng ta không thể tạo một Rectangle object không có tham số (xem lại 2 bài constructor trước để hiểu điều này).

Khi tạo object, nếu các bạn cung cấp tham số như ví dụ dưới đây, thì constructor sẽ thiết lập giá trị thông qua member initializer list:

class Rectangle
{
private:
    double m_length = 1.0;
    double m_width = 1.0;
 
public:
 
    Rectangle(double length, double width)
        : m_length(length), m_width(width)
    {
        // m_length and m_width are initialized by the constructor (the default values aren't used)
    }
 
    void print()
    {
        std::cout << "length: " << m_length << ", width: " << m_width << '\n';
    }
 
};
 
int main()
{
    Rectangle x(2.0, 3.0);
    x.print();
 
    return 0;
}

Kết quả chương trình trên:
length: 2.0, width: 3.0

Chú ý rằng việc thiết lập các biến member sử dụng non-static member initialization yêu cầu phải sử dụng dấu “=” hoặc dấu ngoặc “()” (trong iniform). Và direct initialization form không hoạt động trong phần này.

Rule: Sử dụng non-static member initialization để thiết lập các giá trị mặc định cho các biến member của class.

Bài tập cho phần này. Hãy update chương trình sau sử dụng non-static member initialization member initializer list.

#include <string>
#include <iostream>
class Ball
{
private:
	std::string m_color;
	double m_radius;
 
public:
        // Default constructor with no parameters
	Ball()
	{
		m_color = "black";
		m_radius = 10.0;
	}
 
        // Constructor with only color parameter (radius will use default value)
	Ball(const std::string &color)
	{
		m_color = color;
		m_radius = 10.0;
	}
 
        // Constructor with only radius parameter (color will use default value)
	Ball(double radius)
	{
		m_color = "black";
		m_radius = radius;
	}
 
        // Constructor with both color and radius parameters
	Ball(const std::string &color, double radius)
	{
		m_color = color;
		m_radius = radius;
	}
 
	void print()
	{
		std::cout << "color: " << m_color << ", radius: " << m_radius << '\n';
	}
};
 
int main()
{
	Ball def;
	def.print();
 
	Ball blue("blue");
	blue.print();
	
	Ball twenty(20.0);
	twenty.print();
	
	Ball blueTwenty("blue", 20.0);
	blueTwenty.print();
 
	return 0;
}

Kết quả chương trình như sau:
color: black, radius: 10
color: blue, radius: 10
color: black, radius: 20
color: blue, radius: 20

Solution như sau:

#include <string>
#include <iostream>
class Ball
{
private:
	std::string m_color = "black";
	double m_radius = 10.0;
 
public:
        // Default constructor with no parameters (color and radius will use default values)
	Ball()
	{
	}
 
        // Constructor with only color parameter (radius will use default value)
	Ball(const std::string &color):
		m_color(color)
	{
	}
 
        // Constructor with only radius parameter (color will use default value)
	Ball(double radius):
		m_radius(radius)
	{
	}
 
        // Constructor with both color and radius parameters
	Ball(const std::string &color, double radius):
		m_color(color), m_radius(radius)
	{
	}
 
	void print()
	{
		std::cout << "color: " << m_color << ", radius: " << m_radius << '\n';
	}
};
 
int main()
{
	Ball def;
	def.print();
 
	Ball blue("blue");
	blue.print();
	
	Ball twenty(20.0);
	twenty.print();
	
	Ball blueTwenty("blue", 20.0);
	blueTwenty.print();
 
	return 0;
}

Và câu hỏi tiếp theo là tại sao chúng ta phải khởi tạo một default constructor rỗng trong chương trình phía trên, trong khi tất cả các biến member được khởi tạo thông qua non-static member initialization?
Các bạn hãy tự trả lời câu hỏi này nhé. Hẹn gặp lại.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s