Value initialization for non aggregate class with user defined constructor - c++

I am going through this question:Aggregates and pods
When an object of class in C++ with user defined default constructor, has only some of it's data members initialized,will the rest of data members be value initialized?
Following is the program I tried resulting in compilation error:
#include <iostream>
using namespace std;
class A {
public:
A() {
i=10;
f = 10.0f;
c = 45;
d = 10.0;
}
void show() {
cout << i << "\t" << f << "\t" << c << "\t" << d<<"\n";
}
private:
int i;
float f;
char c;
double d;
};
int main() {
A a={20,20.0f};
a.show();
}

Your class does not qualify as an Aggregate because it has private non-static data members.
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
EDIT:
For objects of non aggregate classes if only some of the data members are initialized, will the rest be value initialized(assigned with 0)?
The rules are specified in:
C++11 8.5.4 List-initialization [dcl.init.list] Para 3:
List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
— Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).
— Otherwise, if T is a specialization of std::initializer_list, an initializer_list object is constructed as described below and used to initialize the object according to the rules for initialization of an object from a class of the same type (8.5).
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
— Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. —end note ]
— Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.
— Otherwise, if the initializer list has no elements, the object is value-initialized.
— Otherwise, the program is ill-formed.
Your program falls in none of the scenarios mentioned and hence falls under the the ill formed case.

You have to declare constructor with 4 arguments like
A(int i = 10, float f = 10.0f, int c = 45, float d = 10.0f):
i(i), f(f), c(c), d(d)
{
}
Now you can initialize your a variable with braces

Related

Value initialization of nested classes

By the rules of value initialization. Value initialization occurs:
1,5) when a nameless temporary object is created with the initializer
consisting of an empty pair of parentheses or braces (since C++11);
2,6) when an object with dynamic storage duration is created by a
new-expression with the initializer consisting of an empty pair of
parentheses or braces (since C++11);
3,7) when a non-static data
member or a base class is initialized using a member initializer with
an empty pair of parentheses or braces (since C++11);
4) when a named
variable (automatic, static, or thread-local) is declared with the
initializer consisting of a pair of braces.
Trivial example
struct A{
int i;
string s;
A(){};
};
A a{}
cout << a.i << endl // default initialized value
without explicitly declared constructor and left with defaulted default ctor // compiler generated one we get.
struct A{
int i;
string s;
};
A a{};
cout << a.i << endl // zero-initialized value
However using antoher struct.
struct A{
int i;
string s;
};
struct B{
A a;
int c;
};
B a{};
cout << a.a.i << endl // default initialized , even tho we did not , int struct B , declared A a{}.
The value of a.i is zero-initialized, even without using {} / () construct, which goes against rules ( if i am not mistaken ).
Using same logic on struct B:
struct A{
int i;
string s;
};
struct B{
A a;
int c;
};
B b;
cout << b.c << endl; // default inicialized
We get behavior according to rules.
The last example:
struct A
{
int i;
A() { }
};
struct B { A a; };
std::cout << B().a.i << endl;
B().a.i is also zero-initialized while we explicitly declared constructor and its not deleted.
Why are the these values getting zero-initialized? By rules stated here they should be default-initialized not zero-initialized.
Thanks for answers.
It's the difference between A being an aggregate or not.
[dcl.init.aggr] (Emphasis mine)
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal initializers
for non-static data members (9.2), no private or protected non-static data members (Clause 11),
So when A has no declared constructors, saying A a{} has the effect of aggregate initialization
which will construct each member with an empty initialization list:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member
not explicitly initialized shall be initialized from an empty initializer list
So you get int{} and std::string{} which will value-initialize the integer member to zero.
When you do provide a default constructor, the aggregate property is lost and the int member remains uninitialized, so accessing it is considered undefined behavior.
To be specific:
This code is undefined behavior when accessing a.i because you provided a user-defined constructor, so the int i field remains uninitialized after construction:
struct A{
int i;
string s;
A(){};
};
A a{} ;
cout << a.i << endl;
And this code exhibits undefined behavior when accessing b.c because you did not perform list initialization on B b:
struct B{
A a;
int c;
};
B b;
cout << b.c << endl;
All the other code is okay, and will zero-initialize the integer fields. In the scenarios where you are using braces {}, you are performing aggregate-initialization.
The last example is a little tricky because you're performing value-initialization. Since B is an aggregate, it gets zero-initialized ([dcl.init]), in which:
each base-class
subobject is zero-initialized
So again you're okay accessing the integer member of the A subobject.
According to the rules of aggregate initialization, the members are indeed value initialized.
Value initialization:
In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.
The effects of aggregate initialization are:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.

Do uninitialized objects exist in C++?

If all objects have at least one constructor be it default c'tor defined by the compiler or user defined then how can objects be uninitialized.
It is possible to declare objects on which no initializations are performed. These objects do exist, they have an indeterminate value, and using this value is undefined behavior (there is an exception to this rule for chars).
Such object can be created by default-intialization.
This is stated in the c++ standard, (§11.6 Initializers)[dlc.init]:
To default-initialize an object of type T means:
(7.1) — If T is a (possibly cv-qualified) class type (Clause 12), constructors are considered. The applicable
constructors are enumerated (16.3.1.3), and the best one for the initializer () is chosen through overload
resolution (16.3). The constructor thus selected is called, with an empty argument list, to initialize the
object.
(7.2) — If T is an array type, each element is default-initialized.
(7.3) — Otherwise, no initialization is performed.
Nevertheless, static objects are always zero-initialized. So any built-in with dynamic or automatic storage duration may not be initialized, even if it is a suboject;
int i; //zero-initialized
struct A{
int i;
};
struct B
{
B(){};
B(int i)
:i{i}{}
int i;
int j;
};
A a; //a.i is zero-initialized
int main()
{
int j; //not initialized
int k{}; //zero-initialized
A b; //b.i not initialized
int* p = new int; //*p not initialized
A* q = new A; //q->i not initialized
B ab; //ab.i and ab.j not initialized
B ab2{1}; //ab.j not initialized
int xx[10]; //xx's element not initialized.
int l = i; //OK l==0;
int m = j; //undefined behavior (because j is not initialized)
int n = b.i; //undefined behavior
int o = *p; //undefined behavior
int w = q->i; //undefined behavior
int ex = x[0] //undefined behavior
}
For member initialization [class.base.init] may help:
In a non-delegating constructor, if a given potentially constructed subobject is not designated by a mem-
initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer), then
— if the entity is a non-static data member that has a default member initializer (12.2) and either
(9.1.1) — the constructor’s class is a union (12.3), and no other variant member of that union is designated
by a mem-initializer-id or
(9.1.2) — the constructor’s class is not a union, and, if the entity is a member of an anonymous union, no
other member of that union is designated by a mem-initializer-id,
the entity is initialized from its default member initializer as specified in 11.6;
(9.2) — otherwise, if the entity is an anonymous union or a variant member (12.3.1), no initialization is
performed;
(9.3) — otherwise, the entity is default-initialized (11.6)
Members of a trivial anonymous union may also not be initialized.
Also one could ask if an object life-time could begin without any initialization, for exemple by using a reinterpret_cast. The answer is no: reinterpret_cast creating a trivially default-constructible object
The standard doesn't talk about existence of objects, however, there is a concept of lifetimes of objects.
Specifically, from [basic.life]†
The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if the object has non-vacuous initialization, its initialization is complete
With non-vacuous initialization defined as
An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor.
We can conclude that for objects with vacuous initializations (such as ints), their lifetimes begins as soon as their storage is acquired, even if they are left uninitialized.
void foo()
{
int i; // i's lifetime begins after this line, but i is uninitialized
// ...
}
† Links are added for ease of reading, they don't appear in the standard
Use byte array:
alignas(alignof(Mat4)) uint8_t result[sizeof(Mat4)];
// ..
node->updateMatrix( ..., /*result*/ reinterprect_cast<Mat4*>(&result[0]));
The constructors of Mat4 will not trigger.

C++11 strange brace initialization behavior

I don't understand how C++11 brace initialization rules work here.
Having this code:
struct Position_pod {
int x,y,z;
};
class Position {
public:
Position(int x=0, int y=0, int z=0):x(x),y(y),z(z){}
int x,y,z;
};
struct text_descriptor {
int id;
Position_pod pos;
const int &constNum;
};
struct text_descriptor td[3] = {
{0, {465,223}, 123},
{1, {465,262}, 123},
};
int main()
{
return 0;
}
Note, that the array is declared to have 3 elements, but only 2 initializers are provided.
However it compiles without error, which sounds strange, as the last array element's reference member will be uninitialized. Indeed, it has NULL value:
(gdb) p td[2].constNum
$2 = (const int &) #0x0: <error reading variable>
And now the "magic": I changed Position_pod to Position
struct text_descriptor {
int id;
Position_pod pos;
const int &constNum;
};
becomes this:
struct text_descriptor {
int id;
Position pos;
const int &constNum;
};
and now it gives the expected error:
error: uninitialized const member ‘text_descriptor::constNum'
My question: Why it compiles in the first case, when it should give an error (as in the second case).
The difference is, that Position_pod uses C - style brace initialization and Position uses C++11 - style initialization, which call Position's constructor. But how does this affect the possibility to leave a reference member uninitialized?
(Update)
Compiler:
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
It will be clear that
struct text_descriptor td[3] = {
{0, {465,223}, 123},
{1, {465,262}, 123},
};
is list-initialisation, and that the initialiser list is not empty.
C++11 says ([dcl.init.list]p3):
List-initialization of an object or reference of type T is defined as follows:
If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).
...
[dcl.init.aggr]p1:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
td is an array, so it is an aggregate, so aggregate initialisation is performed.
[dcl.init.aggr]p7:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (8.5.4).
This is the case here, so td[2] is initialised from an empty initialiser list, which ([dcl.init.list]p3 again) means it is value-initialised.
Value-initialisation, in turn, means ([dcl.init]p7):
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), ...
if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T's implicitly-declared default constructor is non-trivial, that constructor is
called.
...
Your class text_descriptor is a class with no user-provided constructor, so td[2] is first zero-initialised, and then its constructor is called.
Zero-initialisation means ([dcl.init]p5):
To zero-initialize an object or reference of type T means:
if T is a scalar type (3.9), ...
if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
if T is a (possibly cv-qualified) union type, ...
if T is an array type, ...
if T is a reference type, no initialization is performed.
This is well-defined regardless of text_descriptor's default constructor: it just zero-initialises the non-reference members and sub-members.
Then the default constructor is called, if it is non-trivial. Here's how the default constructor is defined ([special]p5):
A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared
as defaulted (8.4). An implicitly-declared default constructor is an inline public member of its class. A defaulted default constructor for class X is defined as deleted if:
...
any non-static data member with no brace-or-equal-initializer is of reference type,
...
A default constructor is trivial if it is not user-provided and if:
its class has no virtual functions (10.3) and no virtual base classes (10.1), and
no non-static data member of its class has a brace-or-equal-initializer, and
all the direct base classes of its class have trivial default constructors, and
for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
Otherwise, the default constructor is non-trivial.
So, the implicitly defined constructor is deleted, as expected, but it is also trivial, if pos is a POD type (!). Because the constructor is trivial, it is not called. Because the constructor is not called, the fact that it is deleted is not a problem.
This is a gaping hole in C++11, which has since been fixed. It happened to have been fixed to deal with inaccessible trivial default constructors, but the fixed wording also covers deleted trivial default constructors. N4140 (roughly C++14) says in [dcl.init.aggr]p7 (emphasis mine):
if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if
T has a non-trivial default constructor, the object is default-initialized;
As T.C. pointed out in the comments, another DR also changed so that td[2] is still initialised from an empty initialiser list, but that empty initialiser list now implies aggregate initialisation. That, in turn, implies each of td[2]'s members is initialised from an empty initialiser list as well ([dcl.init.aggr]p7 again), so would seem to initialise the reference member from {}.
[dcl.init.aggr]p9 then says (as remyabel had pointed out in a now-deleted answer):
If an incomplete or empty initializer-list leaves a member of reference type uninitialized, the program is ill-formed.
It is unclear to me that this applies to references initialised from the implicit {}, but compilers do interpret it as such, and there's not much else that could be meant by it.
The first version (the one with _pod suffix) still works but gives no error because for the z value, the default value of int is chosen (the 0). Idem for the const int reference.
In the second version, you cannot define a const reference without giving it a value. The compiler gives you an error because later you cannot assign any value to it.
In addition, the compiler you're using plays an important role here, maybe it's a bug, just because you are declaring a class member before an int member.

Confused on how empty user defined constructor will initialize non-static non-POD member variables

I know that default initialization for non-POD types will also default initialize non-static non-POD member variables by calling their default constructor. But I'm not sure exactly how this happens. Here is an example of what I mean:
#include <iostream>
#include <vector>
using namespace std;
class Test2 {
public:
Test2() {cout <<"Here";}
};
class Test {
public:
Test() {}
Test2 i;
};
int main() {
Test foo;
}
The output is:
Here
Based on the C++ standard on initializers (8.5), for default initialization:
— if T is a non-POD class type (clause 9), the default constructor
for T is called (and the initialization is ill-formed if T has no
accessible default constructor);
So given this, I do expect that the default constructor Test() will get called, but my empty default constructor for the class Test does not initialize Test2 i explicitly yet clearly, Test2() is getting called implicitly somehow. What I'm wondering is how this happens?
Similarly, for value initialization (not related to example above), if an empty user defined default constructor does not explicitly zero initialize a POD non-static member variable, how does that variable get zero initialized (which I know it does do)? Since based on the standard, it seems that for value initialization, all that happens when you have a user defined default constructor is that the constructor gets called.
The corresponding part of the C++ standard for value initialization is the following:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the
default constructor for T is called (and the initialization is ill-formed if T has no
accessible default constructor);
This question is similar to c++ empty constructor and member initialization
But the difference is that instead of asking what the end result behavior is, I'd like to know why the end result behavior happens.
In the C++11 standard, section 12.6 paragraph 8:
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized
as specified in 8.5;
otherwise, if the entity is a variant member (9.5), no initialization is performed;
otherwise, the entity is default-initialized (8.5).
You are encountering the third case, where there is no initializer for the member and the member isn't a variant member, so in that case it is default-initialized.
Also, from paragraph 10:
In a non-delegating constructor, initialization proceeds in the following order:
- First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list
(regardless of the order of the mem-initializers).
Then, non-static data members are initialized in the order they were declared in the class definition
(again regardless of the order of the mem-initializers).
Finally, the compound-statement of the constructor body is executed.
Regardless of what you specify in your constructor, the members are going to be initialized just before the body of the constructor is executed.
A mem-initializer-id is the identifier used to refer to a member in a constructor initializer list:
class Test {
public:
Test() : i() {} // Here `i` is a mem-initializer-id
Test2 i;
};
Value initialization of a type with a user-defined default constructor performs no initialization on non-static POD members if they are not explicitly initialized in the constructor. E.g., in this program:
#include <iostream>
using namespace std;
struct Foo {
// Foo has a user-defined default constructor
// that does not initialize i
Foo() {}
int i;
};
int main() {
Foo x{}; // value-initialize x
cout << x.i << endl;
}
x.i is uninitialized. The program therefore technically has undefined behavior, but in this case "undefined behavior" most likely means that it will print an unspecified integer value that is likely not 0.
Language lawyer argument:
§12.6.1p2: "An object of class type can also be initialized by a braced-init-list. List-initialization semantics apply; see 8.5 and 8.5.4."
§8.5.4p3: "List-initialization of an object or reference of type T is defined as follows: ... If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized."
§8.5p7: "To value-initialize an object of type T means: ... if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor)
According to the draft of the C++ standard found at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf, section 12.6.2:
If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
— If the entity is a non-static data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non-static data member of a const-qualified type, the entity class shall have a user-declared default constructor.
— Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.
In other words, if an object of a non-POD class type does not appear in an initializer list, the compiler interprets this as if the object had appeared with its default constructor being called.
Also, note that other types (i.e. primitives and POD types) are not initialized, which is different from what you indicated in your question. Global objects are zero-initialized, but the same isn't true for objects on the stack. Here is a small program I put together to test this:
#include <iostream>
class T
{
public:
T() {}
void put(std::ostream &out)
{
out << "a = " << a << std::endl;
out << "b = " << b << std::endl;
out << "c = " << c << std::endl;
}
private:
int a;
int b;
int c;
};
T t2;
int main()
{
T t;
t.put(std::cout);
t2.put(std::cout);
return 0;
}
Compiling with g++ 4.5.2, I got the following output:
a = 8601256
b = 3
c = 2130567168
a = 0
b = 0
c = 0

default values from not defined constructor

Bjarne wrote:-
For a type T, T() is the notation for the default value , as defined by the default constructor .
What happen when we don't declare default constructor ? For example
using namespace std;
class date{
int n,m;
public:
int day(){return n;}
int month(){return m;}
};//no default constructor
int main()
{
date any =date();
cout<<any.month()<<endl;
cout<<any.day()<<endl;
return 0;
}
Output of this program is 0 and 0 every time i run my program. I haven't declare any default constructor then why there exits a default value i.e. 0?
EDIT-
class date{
int n,m;
public:
date (){
m=1;}
int day(){return n;}
int month(){return m;}
};
int main()
{
date any =date();
cout<<any.month()<<endl;
cout<<any.day()<<endl;
return 0;
}
After reading answers i provide a default constructor but now n is getting garbage value but according to answers it should be 0 as m is out of reach of any other constructor and it is value initialisation as mentioned in answer
The behavior you see is Well-Defined for your class.
How & Why is the behavior Well-Defined?
The rule is:
If you do not provide a no argument constructor the compiler generates one for your program in case your program needs one.
Caveat:
The compiler does not generate the no argument constructor if your program defines any constructor for the class.
As per the C++ Standard an object can be initialized in 3 ways:
Zero Initialization
Default Initialization &
Value Initialization
When, a type name or constructor initializer is followed by () the initialization is through value initialization.
Thus,
date any =date();
^^^
Value Initializes an nameless object and then copies it in to the local object any,
while:
date any;
would be a Default Initialization.
Value Initialization gives an initial value of zero to members that are out of reach of any constructor.
In your program, n and m are beyond the reach of any constructor and hence get initialized to 0.
Answer to Edited Question:
In your edited case, your class provides a no argument constructor, date(), which is capable(& should) initialize members n and m, but this constructor doesn't initialize both the members, So In this case no zero initialization takes place, and the uninitialized members in the object have an Indeterminate(any random) value, further this temporary object is copied to any object which displays the shows indeterminate member values.
For Standerdese Fans:
The rules for object Initialization are aptly defined in:
C++03 Standard 8.5/5:
To zero-initialize an object of type T means:
— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
— if T is a non-union class type, each nonstatic data member and each base-class subobject is zero-initialized;
— if T is a union type, the object’s first named data member is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.
To default-initialize an object of type T means:
— if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, the object is zero-initialized.
To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized
Because compiler, cursing under its breath, generates one for you.
EDIT: Since Als said it does not answer question, I'll elaborate. When you use date any = date();, you call compiler-generated default constructor. This constructor calls default constructors for all base classes and data members. For your data members int default constructor is int(), which sets value to 0. Here is code on ideone.com
#include <iostream>
int main( void )
{
int i = -123;
i = int();
std::cout << i << std::endl;
return( 0 );
}
Program output:
0
From the C++ draft standard (December 1996 Working Paper):
If there is no user-declared constructor for class X, a default constructor is implicitly declared. An implicitly-declared default constructor is an inline public member of its class.